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` `:descricao` free-form-prose
608    /// chart-description scalar accessor every consumer of the top-level
609    /// manifest's Chart.yaml `description:` axis keys off — returns the
610    /// author-declared `:descricao` byte-string verbatim as an
611    /// `Option<&str>`, borrowed from the typed slot's own
612    /// `Option<String>` storage. `None` when the slot is absent (the
613    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
614    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
615    /// omitted slot through a `format!("Generated chart for caixa Servico
616    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
617    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
618    /// and [`caixa-feira`]'s `render_flake` folds it through a
619    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
620    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
621    ///
622    /// The `:descricao` slot carries the universal-axis free-form-prose
623    /// chart-description identifier every kind of caixa emits under
624    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
625    /// supplies) — the typed slot's `Option<String>` accept-set
626    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
627    /// chart-description-shape-invalid rejected through
628    /// [`ManifestError::DescricaoInvalid`] past the shared
629    /// [`crate::render::is_chart_description_shape`] predicate the peer
630    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
631    /// load-bearing downstream consumers:
632    ///
633    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
634    ///     gate binding — the universal-axis identity gate wired at
635    ///     caixa-build time.
636    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
637    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
638    ///     chart's `Chart.yaml` `description:` field, which
639    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
640    ///     `WARNING [chart.metadata.description]: description is required`
641    ///     when absent) and which every registry that ingests the chart
642    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
643    ///     chart's canonical one-line prose descriptor.
644    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
645    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
646    ///     header directly beneath the `# <chart-name>` title, which
647    ///     every author who inspects the rendered chart bundle lands at.
648    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
649    ///     top-level fold — the emitted `flake.nix`'s `description`
650    ///     field, which every Nix consumer (`nix flake show`,
651    ///     `nix flake metadata`, downstream flake-registry ingestors)
652    ///     surfaces as the flake's canonical descriptor.
653    ///
654    /// Prior to this lift the `.descricao` field was accessed inline at
655    /// four production sites — [`Self::validate_descricao`]'s
656    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
657    /// caixa-helm `build_chart_yaml`
658    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
659    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
660    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
661    /// `README.md` header fold, and the caixa-feira `render_flake`
662    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
663    /// `description = ""` fold — four open-coded field-accesses that
664    /// expressed no compile-time link back to the typed slot. A future
665    /// extension of the `:descricao` axis to a richer author surface —
666    /// a per-`:descricao` locale-tagged multi-language descriptor map
667    /// (the "one caixa, N language-tagged prose descriptions" arm
668    /// author-tooling internationalization anticipates), a
669    /// per-registry-target length-and-shape overlay the M4 CR
670    /// materializer resolves per-CR (the "ArtifactHub caps description
671    /// at 512 bytes but the internal registry caps at 256" arm), a
672    /// promotion of the plain `Option<String>` byte-string to a richer
673    /// `ChartDescription` newtype guaranteeing the
674    /// `is_chart_description_shape` predicate at the type level — would
675    /// have had to be threaded through all four open-coded copies in
676    /// lockstep or the validate gate and the three emit paths would
677    /// silently disagree on which prose string a given [`Caixa`]
678    /// resolves to (an author's
679    /// `:descricao "Checkout flow orchestration."` would satisfy
680    /// validate while one of the emit paths silently rendered a stale
681    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
682    /// resolution to a typed method on the substrate primitive means
683    /// every downstream consumer of the caixa's per-`Caixa`
684    /// chart-description surface reaches for exactly one typed dispatch
685    /// — the resolver's accept-set migrates as a unit on any future
686    /// axis addition.
687    ///
688    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
689    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
690    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
691    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
692    /// lift folds on. Same "one typed dispatch on the substrate
693    /// primitive, thin projections at each consumer" discipline the
694    /// peer per-`:placement`
695    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
696    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
697    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
698    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
699    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
700    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
701    /// typed-slot atom axes, extended here to the third outer top-level
702    /// `Caixa` universal-axis surface. Named `descricao()` to match the
703    /// storage field's name; the accessor's identity maps onto the
704    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
705    /// carries. The one remaining universal `Option<String>` slot
706    /// (`:edicao`) folds on this pattern next.
707    #[must_use]
708    pub fn descricao(&self) -> Option<&str> {
709        self.descricao.as_deref()
710    }
711
712    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
713    /// accessor every consumer of the top-level manifest's tatara-lisp
714    /// edition-selector axis keys off — returns the author-declared
715    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
716    /// the typed slot's own `Option<String>` storage. `None` when the
717    /// slot is absent (the canonical "omit the slot to defer to the
718    /// substrate's default edition" shape every existing
719    /// [`caixa-resolver`] integration test fixture carries via
720    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
721    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
722    /// arm by construction, so an author-omitted `:edicao` round-trips
723    /// to a build without triggering the year-shape predicate).
724    ///
725    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
726    /// decimal-year language-edition identifier every kind of caixa
727    /// emits under (CAIXA-SDLC §I — the author-facing surface every
728    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
729    /// accept-set (empty-string rejected through
730    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
731    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
732    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
733    /// onto one load-bearing downstream consumer today
734    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
735    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
736    /// future edition-aware substrate consumer the CAIXA-SDLC §I
737    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
738    /// selector every edition-aware build step keys off, the future
739    /// per-edition compatibility-flag overlay the M4 CR materializer
740    /// resolves per-CR, the peer [`Caixa::template`] canonical
741    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
742    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
743    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
744    /// carry `edicao: Some("2026".into())` by construction).
745    ///
746    /// Prior to this lift the `.edicao` field was accessed inline at
747    /// one production site — [`Self::validate_edicao`]'s
748    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
749    /// open-coded field-access that expressed no compile-time link
750    /// back to the typed slot. A future extension of the `:edicao`
751    /// axis to a richer author surface — a per-`:edicao` known-
752    /// edition allowlist (the future tightening
753    /// [`Self::validate_edicao`]'s docstring acknowledges past the
754    /// structural year-shape floor, rejecting year-shaped values that
755    /// don't name a tatara-lisp edition the substrate actually
756    /// understands — `"1999"` is year-shaped but no `1999` edition
757    /// exists), a per-edition compatibility-flag overlay the M4 CR
758    /// materializer resolves per-CR (the "edition `"2026"` enables
759    /// macro-surface features the sibling `"2018"` gates behind a
760    /// feature flag" arm the edition-selector story anticipates), a
761    /// promotion of the plain `Option<String>` byte-string to a
762    /// richer `CaixaEdition` enum discriminated on year once a sibling
763    /// edition to `"2026"` lands — would have had to be threaded
764    /// through the open-coded copy in lockstep with every future
765    /// edition-aware consumer, or the validate gate and the future
766    /// edition-aware consumer path would silently disagree on which
767    /// edition a given [`Caixa`] resolves to (an author's
768    /// `:edicao "2026"` would satisfy validate while a future
769    /// edition-aware consumer silently defaulted to a stale edition,
770    /// or vice versa). Lifting the resolution to a typed method on
771    /// the substrate primitive means every downstream consumer of the
772    /// caixa's per-`Caixa` edition surface reaches for exactly one
773    /// typed dispatch — the resolver's accept-set migrates as a unit
774    /// on any future axis addition.
775    ///
776    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
777    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
778    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
779    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
780    /// `Option<&str>` scalar" projection pattern this lift folds on.
781    /// Same "one typed dispatch on the substrate primitive, thin
782    /// projections at each consumer" discipline the peer per-`:placement`
783    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
784    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
785    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
786    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
787    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
788    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
789    /// typed-slot atom axes, extended here to close the outer top-level
790    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
791    /// slot. Named `edicao()` to match the storage field's name; the
792    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
793    /// vocabulary the slot's docstring already carries.
794    #[must_use]
795    pub fn edicao(&self) -> Option<&str> {
796        self.edicao.as_deref()
797    }
798
799    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
800    /// label caixa-identity scalar accessor every consumer of the top-
801    /// level manifest's identity axis keys off — returns the author-
802    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
803    /// the typed slot's own `String` storage. Non-optional (`:nome` is
804    /// a required-axis scalar every `defcaixa` form must supply; the
805    /// [`Self::from_lisp`] derive rejects an omitted / non-string
806    /// `:nome` at parse time, so a `Caixa` past parse definitionally
807    /// carries a non-`None` `:nome`).
808    ///
809    /// The `:nome` slot carries the universal-axis DNS-1123-label
810    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
811    /// the primary identity axis every `defcaixa` form supplies
812    /// alongside `:versao` / `:kind`; the substrate-wide identity every
813    /// other typed surface that names a caixa reaches through — `:deps`
814    /// entries, `:membros` entries, `:children` entries, the
815    /// `lareira-<nome>` Helm chart name every per-Servico renderer
816    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
817    /// renderer emits) — the typed slot's `String` accept-set (empty
818    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
819    /// invalid rejected through [`ManifestError::NomeInvalid`] past
820    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
821    /// the peer name axes each land on, joint-length-with-`lareira-`-
822    /// prefix rejected through
823    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
824    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
825    /// load-bearing downstream consumer the substrate carries — the
826    /// two universal-axis validate gates at caixa-build time
827    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
828    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
829    /// derivation every per-Servico renderer keys off, the caixa-helm
830    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
831    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
832    /// `HTTPRoute` per-Aplicacao name axes at
833    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
834    /// [`crate::pleme_program_selector`] /
835    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
836    /// derivations, and every future substrate renderer that emits an
837    /// artifact keyed by the caixa's identity.
838    ///
839    /// Prior to this lift the `.nome` field was accessed inline at a
840    /// dozen production sites across `caixa-core` (the two universal-
841    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
842    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
843    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
844    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
845    /// entry `name:` fold, the `flux_kustomization_source_subtree`
846    /// per-cluster subpath derivation), and `caixa-mesh` (the
847    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
848    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
849    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
850    /// insert) — a dozen open-coded field-accesses that expressed no
851    /// compile-time link back to the typed slot. A future extension of
852    /// the `:nome` axis to a richer author surface — a per-`:nome`
853    /// structured `CaixaIdentity` newtype that carries the joint-
854    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
855    /// enforces at the type level (rather than as a validate-time
856    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
857    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
858    /// `partner-org/checkout` collision" arm the multi-tenant-registry
859    /// story acknowledges), a promotion of the plain `String` byte-
860    /// string to a richer `CaixaNome` newtype discriminated on
861    /// namespace prefix — would have had to be threaded through every
862    /// open-coded copy in lockstep or the two validate gates and the
863    /// dozen emit paths would silently disagree on which identity a
864    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
865    /// would satisfy validate while one of the emit paths silently
866    /// rendered a drifted other identity, or vice versa). Lifting the
867    /// resolution to a typed method on the substrate primitive means
868    /// every downstream consumer of the caixa's per-`Caixa` identity
869    /// surface reaches for exactly one typed dispatch — the resolver's
870    /// accept-set migrates as a unit on any future axis addition.
871    ///
872    /// First outer top-level [`Caixa`] `&str`-return required-scalar
873    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
874    /// projection pattern the sibling per-`Caixa` `:versao` future lift
875    /// folds on. Sibling in shape to the peer per-`:membros`
876    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
877    /// [`crate::aplicacao::WitContract::source`] /
878    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
879    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
880    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
881    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
882    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
883    /// per-sub-struct required-axis accessors carry on the sibling M3
884    /// mesh-slot-atom scalar-value axes, extended here to open the
885    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
886    /// Named `nome()` to match the storage field's name; the accessor's
887    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
888    /// slot's docstring already carries.
889    #[must_use]
890    pub fn nome(&self) -> &str {
891        &self.nome
892    }
893
894    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
895    /// pinned-version scalar accessor every consumer of the top-level
896    /// manifest's version axis keys off — returns the author-declared
897    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
898    /// typed slot's own `String` storage. Non-optional (`:versao` is a
899    /// required-axis scalar every `defcaixa` form must supply alongside
900    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
901    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
902    /// parse definitionally carries a non-`None` `:versao`).
903    ///
904    /// The `:versao` slot carries the universal-axis SemVer-2
905    /// concrete-version body every kind of caixa emits under
906    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
907    /// supplies alongside `:nome` / `:kind`; the substrate-wide
908    /// pinned-version every downstream artifact-emitting consumer
909    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
910    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
911    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
912    /// prefix composes on top of, the programs.yaml entry's `versao:`
913    /// value the `lareira-fleet-programs` aggregator carries onto each
914    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
915    /// tags every substrate-side `skopeo push` writes, the lacre
916    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
917    /// prior-version references peers in the exact same SemVer-2 shape).
918    /// The typed slot's `String` accept-set (empty rejected through
919    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
920    /// through [`ManifestError::VersaoInvalid`] past
921    /// [`semver::Version::parse`]) maps onto every load-bearing
922    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
923    /// universal-axis validate gate at caixa-build time, the
924    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
925    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
926    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
927    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
928    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
929    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
930    /// tag derivation (`format!("{prefix}{versao}")`), and every future
931    /// substrate renderer that emits an artifact keyed by the caixa's
932    /// pinned version.
933    ///
934    /// Prior to this lift the `.versao` field was accessed inline at a
935    /// dozen production sites across `caixa-core` (the universal-axis
936    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
937    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
938    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
939    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
940    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
941    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
942    /// (the `feira publish` git-tag derivation + the `feira app graph` /
943    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
944    /// field-accesses that expressed no compile-time link back to the
945    /// typed slot. A future extension of the `:versao` axis to a richer
946    /// author surface — a per-`:versao` structured `CaixaVersion` at the
947    /// storage layer (the substrate already carries a `CaixaVersion`
948    /// newtype at [`crate::version::CaixaVersion`], deferred until the
949    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
950    /// a per-registry `:versao` immutability overlay the M4 CR
951    /// materializer enforces per-CR, a promotion of the plain `String`
952    /// byte-string to a richer `PinnedVersao` newtype discriminated on
953    /// SemVer-2 pre-release / build-metadata presence — would have had
954    /// to be threaded through every open-coded copy in lockstep or the
955    /// validate gate and the dozen emit paths would silently disagree
956    /// on which version a given [`Caixa`] resolves to (an author's
957    /// `:versao "0.1.0"` would satisfy validate while one of the emit
958    /// paths silently rendered a drifted other version, or vice versa).
959    /// Lifting the resolution to a typed method on the substrate
960    /// primitive means every downstream consumer of the caixa's
961    /// per-`Caixa` pinned-version surface reaches for exactly one typed
962    /// dispatch — the resolver's accept-set migrates as a unit on any
963    /// future axis addition.
964    ///
965    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
966    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
967    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
968    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
969    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
970    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
971    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
972    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
973    /// on the sibling per-typed-slot version-carrier axes, extended here
974    /// to close the second outer top-level [`Caixa`] required-`&str`-
975    /// carrying axis so the two universal-axis identity-carrying
976    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
977    /// share the same "one typed dispatch per axis" discipline. Named
978    /// `versao()` to match the storage field's name; the accessor's
979    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
980    /// slot's docstring already carries.
981    #[must_use]
982    pub fn versao(&self) -> &str {
983        &self.versao
984    }
985
986    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
987    /// closed-set-enum discriminant accessor every consumer of the top-
988    /// level manifest's kind axis keys off — returns the author-declared
989    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
990    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
991    /// (`:kind` is a required-axis discriminant every `defcaixa` form
992    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
993    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
994    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
995    /// variant).
996    ///
997    /// The `:kind` slot carries the universal-axis closed-set typed-
998    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
999    /// §I — the primary shape gate every renderer / verifier /
1000    /// operator branches on; the five variants `Biblioteca` /
1001    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1002    /// the caixa surface into disjoint runtime contracts) — the typed
1003    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1004    /// values through the derive-macro's symbol-arm gate, exhaustively
1005    /// matched at every downstream dispatch site) maps onto every
1006    /// load-bearing downstream consumer the substrate carries:
1007    ///
1008    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1009    ///     predicate — the canonical two-line
1010    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1011    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1012    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1013    ///     ComputeUnit` CR materializer) runs at its entry-point,
1014    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1015    ///     `actual:` field the diagnostic surfaces to name the offending
1016    ///     caixa's variant.
1017    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1018    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1019    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1020    ///     slot columns into their typed sub-spec only when the kind
1021    ///     matches (returns `None` otherwise); the future per-Servico
1022    ///     M2-view composer (`servico_view`) will follow the same shape.
1023    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1024    ///     coherence gate — the `!self.kind.requires_exe()` /
1025    ///     `!self.kind.requires_servicos()` predicates that fence
1026    ///     each code-surface slot from the wrong owning kind.
1027    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1028    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1029    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1030    ///     coherence error carriers (`SupervisorOwnsCode` /
1031    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1032    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1033    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1034    ///     variant in their `kind:` field.
1035    ///
1036    /// Prior to this lift the `.kind` field was accessed inline at
1037    /// twenty-plus production sites across `caixa-core` (the
1038    /// [`crate::render::require_kind`] entry-gate predicate + the
1039    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1040    /// composers, the `declared_foreign_code_slots` per-slot kind-
1041    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1042    /// kind ↔ code-surface predicates + four error carriers) — a score
1043    /// of open-coded field-accesses that expressed no compile-time link
1044    /// back to the typed slot. A future extension of the `:kind` axis
1045    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1046    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1047    /// variant across the wasm-component / legacy-container / native-
1048    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1049    /// kind-overlay the M4 CR materializer resolves per-CR (the
1050    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1051    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1052    /// enum to a richer `KindWithRuntime` discriminated on the
1053    /// component-model world axis — would have had to be threaded
1054    /// through every open-coded copy in lockstep or the entry gate,
1055    /// the view composers, and the layout invariants would silently
1056    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1057    /// the resolution to a typed method on the substrate primitive
1058    /// means every downstream consumer of the caixa's per-`Caixa`
1059    /// kind surface reaches for exactly one typed dispatch — the
1060    /// resolver's accept-set migrates as a unit on any future axis
1061    /// addition.
1062    ///
1063    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1064    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1065    /// required-discriminant" projection pattern. Sibling in shape to
1066    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1067    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1068    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1069    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1070    /// on the sibling nested-spec typed-slot discriminator axes,
1071    /// extended here to the outer top-level [`Caixa`] universal-axis
1072    /// surface. Named `kind()` to match the storage field's name;
1073    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1074    /// vocabulary the slot's docstring already carries.
1075    #[must_use]
1076    pub fn kind(&self) -> CaixaKind {
1077        self.kind
1078    }
1079
1080    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1081    /// maintainer-name-list slice-accessor every consumer of the top-
1082    /// level manifest's maintainer axis keys off — returns the author-
1083    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1084    /// the same backing buffer the raw `self.autores.as_slice()` field
1085    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1086    /// empty axis every `defcaixa` form supplies with an empty `()` when
1087    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1088    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1089    /// parse definitionally carries a `Vec<String>` slot — possibly
1090    /// empty — and the returned `&[String]` degenerates to an empty
1091    /// slice on that arm without any silent `None` collapse).
1092    ///
1093    /// The `:autores` slot carries the universal-axis maintainer-name
1094    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1095    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1096    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1097    /// every downstream registry-facing artifact emits under) — the
1098    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1099    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1100    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1101    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1102    /// onto every load-bearing downstream consumer the substrate carries
1103    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1104    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1105    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1106    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1107    /// name, email: None }` record, every future per-`Caixa` registry-
1108    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1109    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1110    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1111    /// the future per-cluster author-notification overlay the M4 CR
1112    /// materializer resolves per-CR).
1113    ///
1114    /// Prior to this lift the `.autores` field was accessed inline at
1115    /// two production sites — [`Self::validate_autores`]'s `for autor
1116    /// in &self.autores` walk that gates every entry through
1117    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1118    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1119    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1120    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1121    /// two open-coded field-accesses that expressed no compile-time link
1122    /// back to the typed slot. A future extension of the `:autores` axis
1123    /// to a richer author surface — a per-`:autores` structured
1124    /// `Maintainer { name, email, url }` at the storage layer once the
1125    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1126    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1127    /// enforces per-CR (the "cluster policy demands every author declare
1128    /// an on-file `mailto:` contact" arm), a promotion of the plain
1129    /// `Vec<String>` byte-string list to a richer
1130    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1131    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1132    /// predicate already resolves through — would have had to be
1133    /// threaded through both open-coded copies in lockstep or the
1134    /// validate gate and the caixa-helm emit path would silently
1135    /// disagree on which authors a given [`Caixa`] resolves to (an
1136    /// author's `:autores ("alice" "bob")` would satisfy validate while
1137    /// the caixa-helm emit path silently rendered a drifted other
1138    /// maintainer list, or vice versa). Lifting the resolution to a
1139    /// typed method on the substrate primitive means every downstream
1140    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1141    /// for exactly one typed dispatch — the resolver's accept-set
1142    /// migrates as a unit on any future axis addition.
1143    ///
1144    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1145    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1146    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1147    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1148    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1149    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1150    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1151    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1152    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1153    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1154    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1155    /// per-M3 typed-slot list axes, extended here to the outer top-level
1156    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1157    /// `&Vec<String>`) because every downstream consumer of the author
1158    /// list treats it as a read-only sequence — the slice-view is the
1159    /// narrowest borrow that supports every present + roadmapped consumer
1160    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1161    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1162    /// reaches for (the storage-side `Vec` remains reachable through the
1163    /// `pub autores` field for the mutation-carrying serde round-trip and
1164    /// per-test fixture-mutation paths). Named `autores()` to match the
1165    /// storage field's name; the accessor's identity maps onto the
1166    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1167    /// carries.
1168    #[must_use]
1169    pub fn autores(&self) -> &[String] {
1170        self.autores.as_slice()
1171    }
1172
1173    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1174    /// registry-search-tag-list slice-accessor every consumer of the
1175    /// top-level manifest's topical-tag axis keys off — returns the
1176    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1177    /// slice-view over the same backing buffer the raw
1178    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1179    /// list-carrying (`:etiquetas` is a default-empty axis every
1180    /// `defcaixa` form supplies with an empty `()` when unset; the
1181    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1182    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1183    /// definitionally carries a `Vec<String>` slot — possibly empty —
1184    /// and the returned `&[String]` degenerates to an empty slice on
1185    /// that arm without any silent `None` collapse).
1186    ///
1187    /// The `:etiquetas` slot carries the universal-axis topical-tag
1188    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1189    /// author-facing surface every `defcaixa` form supplies alongside
1190    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1191    /// search-facing axis every downstream registry-facing artifact
1192    /// emits under) — the typed slot's `Vec<String>` accept-set
1193    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1194    /// non-chart-keyword-shape rejected through
1195    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1196    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1197    /// every load-bearing downstream consumer the substrate carries —
1198    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1199    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1200    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1201    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1202    /// `Chart.yaml` `keywords:` array (chained with the
1203    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1204    /// dedup'd through a `BTreeSet` at emit time), every future per-
1205    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1206    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1207    /// annotation, the future per-cluster tag-notification overlay the
1208    /// M4 CR materializer resolves per-CR).
1209    ///
1210    /// Prior to this lift the `.etiquetas` field was accessed inline at
1211    /// two production sites — [`Self::validate_etiquetas`]'s `for
1212    /// etiqueta in &self.etiquetas` walk that gates every entry through
1213    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1214    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1215    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1216    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1217    /// two open-coded field-accesses that expressed no compile-time
1218    /// link back to the typed slot. A future extension of the
1219    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1220    /// structured `ChartKeyword { name, uri, category }` at the storage
1221    /// layer once the substrate absorbs `artifacthub.io/keywords`
1222    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1223    /// CR materializer enforces per-CR (the "cluster policy demands
1224    /// every tag come from a substrate-approved taxonomy" arm), a
1225    /// promotion of the plain `Vec<String>` byte-string list to a
1226    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1227    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1228    /// already resolves through — would have had to be threaded through
1229    /// both open-coded copies in lockstep or the validate gate and the
1230    /// caixa-helm emit path would silently disagree on which tags a
1231    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1232    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1233    /// path silently rendered a drifted other keyword list, or vice
1234    /// versa). Lifting the resolution to a typed method on the
1235    /// substrate primitive means every downstream consumer of the
1236    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1237    /// typed dispatch — the resolver's accept-set migrates as a unit
1238    /// on any future axis addition.
1239    ///
1240    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1241    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1242    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1243    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1244    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1245    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1246    /// fold onto the same pattern in future lifts. Sibling in shape to
1247    /// the peer per-`:supervisor`
1248    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1249    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1250    /// (a6e18d7), per-`:membros`
1251    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1252    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1253    /// (0dcc926), and per-`:upgrade-from :instructions`
1254    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1255    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1256    /// typed-slot list axes, extended here to the outer top-level
1257    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1258    /// `&Vec<String>`) because every downstream consumer of the tag
1259    /// list treats it as a read-only sequence — the slice-view is the
1260    /// narrowest borrow that supports every present + roadmapped
1261    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1262    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1263    /// the typed view reaches for (the storage-side `Vec` remains
1264    /// reachable through the `pub etiquetas` field for the mutation-
1265    /// carrying serde round-trip and per-test fixture-mutation paths).
1266    /// Named `etiquetas()` to match the storage field's name; the
1267    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1268    /// vocabulary the slot's docstring already carries.
1269    #[must_use]
1270    pub fn etiquetas(&self) -> &[String] {
1271        self.etiquetas.as_slice()
1272    }
1273
1274    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1275    /// library-source-path-list slice-accessor every consumer of the
1276    /// top-level manifest's Biblioteca-source axis keys off — returns
1277    /// the author-declared `:bibliotecas` list verbatim as a
1278    /// `&[String]` slice-view over the same backing buffer the raw
1279    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1280    /// list-carrying (`:bibliotecas` is a default-empty axis every
1281    /// `defcaixa` form supplies with an empty `()` when unset; the
1282    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1283    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1284    /// parse definitionally carries a `Vec<String>` slot — possibly
1285    /// empty — and the returned `&[String]` degenerates to an empty
1286    /// slice on that arm without any silent `None` collapse).
1287    ///
1288    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1289    /// entry-path list every `:kind Biblioteca` caixa emits under
1290    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1291    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1292    /// substrate-wide library-carrier axis every downstream
1293    /// authoring-facing consumer keys off) — the typed slot's
1294    /// `Vec<String>` accept-set (empty-per-entry rejected through
1295    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1296    /// non-sandboxed-relative-shape rejected through
1297    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1298    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1299    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1300    /// maps onto every load-bearing downstream consumer the substrate
1301    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1302    /// empty-check + per-entry file-exists loop at
1303    /// caixa-core/src/layout.rs that gates each entry through
1304    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1305    /// [`Self::validate_code_paths`] per-slot shape gate at
1306    /// caixa-core/src/manifest.rs that walks each entry through the
1307    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1308    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1309    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1310    /// declared library file for lexical / structural errors before
1311    /// downstream `importar` resolution, every future per-`Caixa`
1312    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1313    /// (the future `tatara-lispc` compilation entry the docstring at
1314    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1315    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1316    /// the future `caixa-lsp` per-library semantic-token stream the
1317    /// caixa-lsp docstring roadmaps).
1318    ///
1319    /// Prior to this lift the `.bibliotecas` field was accessed inline
1320    /// at three production sites — [`crate::LayoutInvariants`]'s
1321    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1322    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1323    /// declared library path through the on-disk-existence check,
1324    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1325    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1326    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1327    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1328    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1329    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1330    /// coded field-accesses that expressed no compile-time link back
1331    /// to the typed slot. A future extension of the `:bibliotecas`
1332    /// axis to a richer library surface — a per-`:bibliotecas`
1333    /// structured `BibliotecaEntry { path, edition, exports }` at the
1334    /// storage layer once the substrate absorbs the per-library
1335    /// language-edition + explicit-exports tuple the tatara-lisp
1336    /// module-system roadmap acknowledges, a per-registry
1337    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1338    /// per-CR (the "cluster policy demands every biblioteca declare
1339    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1340    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1341    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1342    /// [`crate::render::is_sandboxed_relative_path`] +
1343    /// [`crate::render::is_lisp_extension`] predicates already resolve
1344    /// through — would have had to be threaded through all three
1345    /// open-coded copies in lockstep or the layout gate, the shape
1346    /// validator, and the `feira build` phase-1 parse walk would
1347    /// silently disagree on which library paths a given [`Caixa`]
1348    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1349    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1350    /// silently parsed a drifted other list, or vice versa). Lifting
1351    /// the resolution to a typed method on the substrate primitive
1352    /// means every downstream consumer of the caixa's per-`Caixa`
1353    /// library-source surface reaches for exactly one typed dispatch
1354    /// — the resolver's accept-set migrates as a unit on any future
1355    /// axis addition.
1356    ///
1357    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1358    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1359    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1360    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1361    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1362    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1363    /// `:children` / `:membros` / `:contratos`) fold onto the same
1364    /// pattern in future lifts. Sibling in shape to the peer
1365    /// per-`:supervisor`
1366    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1367    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1368    /// (a6e18d7), per-`:membros`
1369    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1370    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1371    /// (0dcc926), and per-`:upgrade-from :instructions`
1372    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1373    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1374    /// typed-slot list axes, extended here to the outer top-level
1375    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1376    /// `&Vec<String>`) because every downstream consumer of the
1377    /// library-source list treats it as a read-only sequence — the
1378    /// slice-view is the narrowest borrow that supports every
1379    /// present + roadmapped consumer (`.iter()`, `.len()`,
1380    /// `.is_empty()`) without leaking the backing `Vec`'s
1381    /// grow/push/reserve surface no consumer of the typed view
1382    /// reaches for (the storage-side `Vec` remains reachable through
1383    /// the `pub bibliotecas` field for the mutation-carrying serde
1384    /// round-trip and per-test fixture-mutation paths). Named
1385    /// `bibliotecas()` to match the storage field's name; the
1386    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1387    /// vocabulary the slot's docstring already carries.
1388    #[must_use]
1389    pub fn bibliotecas(&self) -> &[String] {
1390        self.bibliotecas.as_slice()
1391    }
1392
1393    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1394    /// nix-built-executable-entry-path-list slice-accessor every consumer
1395    /// of the top-level manifest's Binario-executable axis keys off —
1396    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1397    /// slice-view over the same backing buffer the raw
1398    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1399    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1400    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1401    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1402    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1403    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1404    /// degenerates to an empty slice on that arm without any silent
1405    /// `None` collapse).
1406    ///
1407    /// The `:exe` slot carries the universal-axis nix-built executable
1408    /// entry-path list every `:kind Binario` caixa emits under
1409    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1410    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1411    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1412    /// downstream flake-build-facing consumer keys off) — the typed
1413    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1414    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1415    /// non-sandboxed-relative-shape rejected through
1416    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1417    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1418    /// directory paths rejected past the layout's
1419    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1420    /// onto every load-bearing downstream consumer the substrate carries
1421    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1422    /// per-entry file-exists + `exe/`-directory-fence loop at
1423    /// caixa-core/src/layout.rs that gates each entry through
1424    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1425    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1426    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1427    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1428    /// that fences code-surface slots off from the two no-code kinds,
1429    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1430    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1431    /// fences the `:exe` code surface off from every non-Binario code-
1432    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1433    /// that walks each entry through the sandbox-relative / cross-entry
1434    /// duplicate gates, every future per-`Caixa` executable-facing
1435    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1436    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1437    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1438    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1439    /// future `feira nix` per-executable Binario-target emit path).
1440    ///
1441    /// Prior to this lift the `.exe` field was accessed inline at three
1442    /// production sites — the compound-code-path `has_code =
1443    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1444    /// !caixa.servicos.is_empty()` OR-fold on the
1445    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1446    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1447    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1448    /// gate, the per-entry `for p in &caixa.exe`
1449    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1450    /// [`Self::declared_foreign_code_slots`]'s
1451    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1452    /// open-coded field-accesses that expressed no compile-time link
1453    /// back to the typed slot. A future extension of the `:exe` axis
1454    /// to a richer executable surface — a per-`:exe` structured
1455    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1456    /// layer once the substrate absorbs the per-executable
1457    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1458    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1459    /// the M4 CR materializer enforces per-CR (the "cluster policy
1460    /// demands every Binario declare an explicit `:wrapper`" arm), a
1461    /// promotion of the plain `Vec<String>` byte-string list to a
1462    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1463    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1464    /// fence already resolves through — would have had to be threaded
1465    /// through all four open-coded copies in lockstep or the layout
1466    /// gate, the shape validator, and the `feira nix` emit path would
1467    /// silently disagree on which executable paths a given [`Caixa`]
1468    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1469    /// satisfy layout while `feira nix` silently packaged a drifted
1470    /// other list, or vice versa). Lifting the resolution to a typed
1471    /// method on the substrate primitive means every downstream
1472    /// consumer of the caixa's per-`Caixa` executable-source surface
1473    /// reaches for exactly one typed dispatch — the resolver's accept-
1474    /// set migrates as a unit on any future axis addition.
1475    ///
1476    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1477    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1478    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1479    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1480    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1481    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1482    /// future lift closes onto (per the trio of code-surface list slots
1483    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1484    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1485    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1486    /// last unlifted code-surface slot). Sibling in shape to the peer
1487    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1488    /// (bc92bce), per-`:placement`
1489    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1490    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1491    /// (6c77e36), per-`:contratos`
1492    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1493    /// per-`:upgrade-from :instructions`
1494    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1495    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1496    /// typed-slot list axes, extended here to the outer top-level
1497    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1498    /// `&Vec<String>`) because every downstream consumer of the
1499    /// executable-source list treats it as a read-only sequence — the
1500    /// slice-view is the narrowest borrow that supports every
1501    /// present + roadmapped consumer (`.iter()`, `.len()`,
1502    /// `.is_empty()`) without leaking the backing `Vec`'s
1503    /// grow/push/reserve surface no consumer of the typed view
1504    /// reaches for (the storage-side `Vec` remains reachable through
1505    /// the `pub exe` field for the mutation-carrying serde
1506    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1507    /// to match the storage field's name; the accessor's identity
1508    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1509    /// docstring already carries.
1510    #[must_use]
1511    pub fn exe(&self) -> &[String] {
1512        self.exe.as_slice()
1513    }
1514
1515    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1516    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1517    /// of the top-level manifest's Servico-component axis keys off —
1518    /// returns the author-declared `:servicos` list verbatim as a
1519    /// `&[String]` slice-view over the same backing buffer the raw
1520    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1521    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1522    /// form supplies with an empty `()` when unset; the
1523    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1524    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1525    /// definitionally carries a `Vec<String>` slot — possibly empty —
1526    /// and the returned `&[String]` degenerates to an empty slice on
1527    /// that arm without any silent `None` collapse).
1528    ///
1529    /// The `:servicos` slot carries the universal-axis
1530    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1531    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1532    /// author-facing surface every `defcaixa` form supplies alongside
1533    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1534    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1535    /// Servico-facing renderer keys off) — the typed slot's
1536    /// `Vec<String>` accept-set (empty-per-entry rejected through
1537    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1538    /// non-sandboxed-relative-shape rejected through
1539    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1540    /// extension rejected through
1541    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1542    /// entry duplicate rejected through
1543    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1544    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1545    /// renderer entry-points, out-of-`servicos/`-directory paths
1546    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1547    /// `starts_with` fence) maps onto every load-bearing downstream
1548    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1549    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1550    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1551    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1552    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1553    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1554    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1555    /// that fences code-surface slots off from the two no-code kinds,
1556    /// [`Self::declared_foreign_code_slots`]'s
1557    /// `!self.servicos.is_empty()` arm on the
1558    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1559    /// `:servicos` code surface off from every non-Servico code-running
1560    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1561    /// walks each entry through the sandbox-relative / `.computeunit.
1562    /// yaml`-extension / cross-entry duplicate gates, the
1563    /// [`crate::require_single_servico`] V0 singularity gate every
1564    /// per-Servico renderer entry-point runs through
1565    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1566    /// `feira deploy` per-verb `first_servico_path` walk at
1567    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1568    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1569    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1570    /// per-Servico OCI packager, the future M4
1571    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1572    /// per-Servico OTel collector-config emit).
1573    ///
1574    /// Prior to this lift the `.servicos` field was accessed inline at
1575    /// five production sites — the compound-code-path `has_code =
1576    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1577    /// !caixa.servicos.is_empty()` OR-fold on the
1578    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1579    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1580    /// `caixa.servicos.is_empty()`
1581    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1582    /// per-entry `for p in &caixa.servicos`
1583    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1584    /// [`Self::declared_foreign_code_slots`]'s
1585    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1586    /// and the [`crate::require_single_servico`] V0 count gate's
1587    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1588    /// projection (both the accept-arm predicate and the
1589    /// diagnostic-carrying `ServicoCountMismatch { count }`
1590    /// projection) — five open-coded field-accesses across three
1591    /// crates that expressed no compile-time link back to the typed
1592    /// slot. A future extension of the `:servicos` axis to a richer
1593    /// component surface — a per-`:servicos` structured
1594    /// `ServicoEntry { path, world, capabilities }` at the storage
1595    /// layer once the substrate absorbs the per-component WIT-world +
1596    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1597    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1598    /// materializer enforces per-CR (the "cluster policy demands every
1599    /// Servico declare an explicit `:world`" arm), a promotion of the
1600    /// plain `Vec<String>` byte-string list to a richer
1601    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1602    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1603    /// `starts_with(servicos_dir)` fence and the
1604    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1605    /// already resolve through, a promotion of the V0 singleton
1606    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1607    /// component-model multi-world boundary — would have had to be
1608    /// threaded through all five open-coded copies in lockstep or the
1609    /// layout gate, the shape validator, the V0 count gate, and the
1610    /// `feira chart` / `feira deploy` entry-point walks would silently
1611    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1612    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1613    /// yaml")` would satisfy layout while `feira chart` silently
1614    /// packaged a drifted other list, or vice versa). Lifting the
1615    /// resolution to a typed method on the substrate primitive means
1616    /// every downstream consumer of the caixa's per-`Caixa`
1617    /// ComputeUnit-CR-source surface reaches for exactly one typed
1618    /// dispatch — the resolver's accept-set migrates as a unit on any
1619    /// future axis addition.
1620    ///
1621    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1622    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1623    /// projection pattern [`Self::autores`] (b5d813f) opened,
1624    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1625    /// (8a36c23) closed the universal-axis text-tag family of, and
1626    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1627    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1628    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1629    /// a substrate-canonical slice accessor, the trio of code-surface
1630    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1631    /// tuple carries is complete on the typed dispatch surface (the
1632    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1633    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1634    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1635    /// per-element accessor swap in isolation — a future companion lift
1636    /// promotes the tuple's element type to `&[String]` and threads the
1637    /// triple of typed dispatches through as a unit). Sibling in shape
1638    /// to the peer per-`:supervisor`
1639    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1640    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1641    /// (a6e18d7), per-`:membros`
1642    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1643    /// per-`:contratos`
1644    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1645    /// per-`:upgrade-from :instructions`
1646    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1647    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1648    /// typed-slot list axes, extended here to the outer top-level
1649    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1650    /// `&Vec<String>`) because every downstream consumer of the
1651    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1652    /// the slice-view is the narrowest borrow that supports every
1653    /// present + roadmapped consumer (`.iter()`, `.len()`,
1654    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1655    /// grow/push/reserve surface no consumer of the typed view reaches
1656    /// for (the storage-side `Vec` remains reachable through the
1657    /// `pub servicos` field for the mutation-carrying serde round-trip
1658    /// and per-test fixture-mutation paths, and for the
1659    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1660    /// homogeneous-element-type shape carries the raw field access
1661    /// until the trio-closure lift promotes the tuple as a unit).
1662    /// Named `servicos()` to match the storage field's name; the
1663    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1664    /// vocabulary the slot's docstring already carries.
1665    #[must_use]
1666    pub fn servicos(&self) -> &[String] {
1667        self.servicos.as_slice()
1668    }
1669
1670    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
1671    /// runtime-dependency-declaration-list slice-accessor every consumer
1672    /// of the top-level manifest's runtime-dep-graph axis keys off —
1673    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
1674    /// slice-view over the same backing buffer the raw
1675    /// `self.deps.as_slice()` field access borrows from. Empty-list-
1676    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
1677    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1678    /// derive folds an omitted `:deps` through `#[serde(default)]` to
1679    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1680    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
1681    /// degenerates to an empty slice on that arm without any silent
1682    /// `None` collapse).
1683    ///
1684    /// The `:deps` slot carries the universal-axis runtime dependency
1685    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1686    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1687    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
1688    /// every downstream resolver-facing artifact emits under) — the
1689    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
1690    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
1691    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
1692    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
1693    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
1694    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
1695    /// maps onto every load-bearing downstream consumer the substrate
1696    /// carries — the [`Self::validate_deps`] per-entry
1697    /// [`Dep::validate`] + within-list dedup walk at
1698    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
1699    /// cross-list self-reference gate at caixa-core/src/layout.rs that
1700    /// checks each entry against the caixa's own `:nome`, the
1701    /// caixa-resolver `for dep in &root.deps` closure walk at
1702    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
1703    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
1704    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
1705    /// caixa-crd/src/conversion.rs that materializes each entry into the
1706    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
1707    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1708    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
1709    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
1710    /// closure emit walk the caixa-resolver docstring roadmaps).
1711    ///
1712    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1713    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1714    /// sibling `:deps-dev` future lift closes on. Peer of the closed
1715    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
1716    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
1717    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
1718    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
1719    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
1720    /// pattern onto a novel element-type axis (`Dep` composite vs the
1721    /// prior sibling family's `String` scalar). Sibling in shape to the
1722    /// peer per-`:supervisor`
1723    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1724    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1725    /// (a6e18d7), per-`:membros`
1726    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1727    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1728    /// (0dcc926), and per-`:upgrade-from :instructions`
1729    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1730    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1731    /// typed-slot list axes, extended here to the outer top-level
1732    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
1733    /// (not `&Vec<Dep>`) because every downstream consumer of the
1734    /// runtime-dep list treats it as a read-only sequence — the slice-
1735    /// view is the narrowest borrow that supports every present +
1736    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1737    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1738    /// of the typed view reaches for (the storage-side `Vec` remains
1739    /// reachable through the `pub deps` field for the mutation-carrying
1740    /// serde round-trip and per-test fixture-mutation paths). Named
1741    /// `deps()` to match the storage field's name; the accessor's
1742    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1743    /// slot's docstring already carries.
1744    #[must_use]
1745    pub fn deps(&self) -> &[Dep] {
1746        self.deps.as_slice()
1747    }
1748
1749    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
1750    /// development-only-dependency-declaration-list slice-accessor every
1751    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
1752    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
1753    /// slice-view over the same backing buffer the raw
1754    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
1755    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
1756    /// form supplies with an empty `()` when unset; the
1757    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
1758    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1759    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
1760    /// the returned `&[Dep]` degenerates to an empty slice on that arm
1761    /// without any silent `None` collapse).
1762    ///
1763    /// The `:deps-dev` slot carries the universal-axis dev-only
1764    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
1765    /// the author-facing sibling of `:deps` that every `defcaixa` form
1766    /// supplies to declare tests / lint / bench closures the runtime
1767    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
1768    /// axis every downstream test-facing artifact emits under, matching
1769    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
1770    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
1771    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
1772    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
1773    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
1774    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
1775    /// within-list duplicate `:nome` rejected through
1776    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
1777    /// load-bearing downstream consumer the substrate carries — the
1778    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
1779    /// dedup walk at caixa-core/src/manifest.rs, the
1780    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
1781    /// gate at caixa-core/src/layout.rs that checks each entry against
1782    /// the caixa's own `:nome`, the caixa-resolver
1783    /// `for dep in &root.deps_dev` closure walk at
1784    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
1785    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
1786    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
1787    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
1788    /// overlay the M4 CR materializer resolves per-CR, the future
1789    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
1790    /// roadmaps).
1791    ///
1792    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
1793    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
1794    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
1795    /// jointly close the two-list dep-graph surface every downstream
1796    /// resolver-facing consumer keys off (runtime `:deps` +
1797    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
1798    /// pair the [`Self::validate_deps`] gate already walks in canonical
1799    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
1800    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
1801    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
1802    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
1803    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
1804    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
1805    /// dev-dep composite-element axis (`Dep` composite, matching the
1806    /// [`Self::deps`] element type). Sibling in shape to the peer
1807    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1808    /// (bc92bce), per-`:placement`
1809    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1810    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1811    /// (6c77e36), per-`:contratos`
1812    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1813    /// per-`:upgrade-from :instructions`
1814    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1815    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1816    /// typed-slot list axes, folded here to the outer top-level
1817    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
1818    /// (not `&Vec<Dep>`) because every downstream consumer of the
1819    /// dev-dep list treats it as a read-only sequence — the slice-view
1820    /// is the narrowest borrow that supports every present +
1821    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
1822    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
1823    /// of the typed view reaches for (the storage-side `Vec` remains
1824    /// reachable through the `pub deps_dev` field for the mutation-
1825    /// carrying serde round-trip and per-test fixture-mutation paths).
1826    /// Named `deps_dev()` to match the storage field's `snake_case` name;
1827    /// the kebab-case author-surface tag `:deps-dev` is the same axis
1828    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
1829    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1830    /// docstring already carries.
1831    #[must_use]
1832    pub fn deps_dev(&self) -> &[Dep] {
1833        self.deps_dev.as_slice()
1834    }
1835
1836    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
1837    /// every consumer that walks one of the two dep-list axes keyed on a
1838    /// [`crate::dep::DepList`] discriminant reaches for — routes the
1839    /// `(list: DepList) -> &[Dep]` projection through one typed method on
1840    /// the substrate primitive rather than the prior open-coded
1841    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
1842    /// inline dispatch every per-axis walker would otherwise carry.
1843    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
1844    /// `&[Dep]` slice-view over the same backing buffer the sibling
1845    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
1846    /// accessors borrow from, preserving the empty-list-carrying invariant
1847    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
1848    /// are default-empty axes every `defcaixa` form supplies with an empty
1849    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
1850    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
1851    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
1852    /// returned `&[Dep]` degenerates to an empty slice on either arm
1853    /// without any silent `None` collapse).
1854    ///
1855    /// The [`crate::dep::DepList`] closed-set typed enum is the
1856    /// substrate's canonical discriminator for the "runtime-closure
1857    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
1858    /// consumer dispatches on — the compiler-checked exhaustiveness on
1859    /// the enum's `match` arms is the build-time guarantee that no future
1860    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
1861    /// that a future third dep-list axis (a `:deps-build` build-only
1862    /// closure once the substrate grows cross-artifact heterogeneous
1863    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
1864    /// consumer. Prior to this the read side carried two per-slot
1865    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
1866    /// typed dispatch that a per-axis walker could parametrise on, so
1867    /// every per-list walker (the [`Self::validate_deps`] per-list
1868    /// [`crate::render::insert_first_seen`] dedup walk, a future
1869    /// `feira app graph` per-list dep summary, a future M4 per-cluster
1870    /// dev-closure-audit overlay the CR materializer resolves per-CR)
1871    /// open-coded the same two-block "run over `:deps`, then run over
1872    /// `:deps-dev`" pattern — a silent duplication that a future third
1873    /// dep-list axis would have had to grow a third block at every site.
1874    ///
1875    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
1876    /// (359fba5) — closes the two-side dispatch symmetry on the outer
1877    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
1878    /// side, `deps_of` on the read side, both keyed on the same
1879    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
1880    /// the substrate primitive, thin projections at each consumer"
1881    /// discipline the sibling per-slot read accessors ([`Self::nome`]
1882    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
1883    /// the outer-[`Caixa`] typed-dispatch read surface.
1884    #[must_use]
1885    pub fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
1886        match list {
1887            crate::dep::DepList::Prod => self.deps(),
1888            crate::dep::DepList::Dev => self.deps_dev(),
1889        }
1890    }
1891
1892    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
1893    /// consumer that appends to one of the two dep-list axes keys off
1894    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
1895    /// method on the substrate primitive rather than the prior
1896    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
1897    /// else { &mut caixa.deps }` inline dispatch + open-coded
1898    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
1899    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
1900    /// a within-list name collision — the same `list: &'static str`
1901    /// diagnostic shape [`Self::validate_deps`]'s per-list
1902    /// [`crate::render::insert_first_seen`] walk raises on the peer
1903    /// parse-time within-list dedup axis, so a future author reading a
1904    /// `feira add` refusal and a `feira build` refusal reaches for the
1905    /// same corrective surface without switching diagnostic idioms.
1906    ///
1907    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
1908    /// closed-set typed carrier for the "runtime-closure `:deps` vs
1909    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
1910    /// dispatches on — the compiler-checked exhaustiveness on the
1911    /// enum's `match` arms is the build-time guarantee that no future
1912    /// per-list mutation-site regresses to a bare-`bool`-flag
1913    /// (`is_dev: bool`) inline dispatch that a future third
1914    /// dep-list axis (a `:deps-build` build-only closure once the
1915    /// substrate grows cross-artifact heterogeneous dep-graphs, per
1916    /// CAIXA-SDLC §I) would silently split at every consumer.
1917    ///
1918    /// Same "one typed dispatch on the substrate primitive, thin
1919    /// projections at each consumer" discipline the sibling per-slot
1920    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
1921    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
1922    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
1923    /// the substrate's first typed-mutation dispatch on the top-level
1924    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
1925    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
1926    /// diagnostic path routed no through-line back to the typed slot,
1927    /// so a future extension of either dep-list axis to a richer author
1928    /// surface (a per-cluster override the operator pins through a
1929    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
1930    /// roadmap acknowledges, an M4
1931    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
1932    /// admission-webhook that normalized the list at admission time)
1933    /// would have had to be threaded through the `feira add` mutation
1934    /// site in lockstep with every read consumer or one path would
1935    /// silently disagree with the other on which list a given dep lands
1936    /// in. Lifting the resolution rule to a typed method on the
1937    /// substrate primitive means every downstream dep-list-mutating
1938    /// consumer of the top-level manifest reaches for exactly one typed
1939    /// dispatch — the resolver's accept-set migrates as a unit on any
1940    /// future axis addition.
1941    ///
1942    /// # Errors
1943    ///
1944    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
1945    /// when another entry in the same list already carries the same
1946    /// `:nome` — the mutation is refused and the caller can surface the
1947    /// typed diagnostic to the author (the `feira add` verb routes the
1948    /// error through `anyhow::Error::from`, which preserves the
1949    /// canonical `#[error(...)]`-templated diagnostic body).
1950    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
1951        let target = match list {
1952            crate::dep::DepList::Prod => &mut self.deps,
1953            crate::dep::DepList::Dev => &mut self.deps_dev,
1954        };
1955        if target.iter().any(|d| d.nome() == dep.nome()) {
1956            return Err(DepError::DuplicateNome {
1957                nome: dep.nome().to_string(),
1958                list: list.as_str(),
1959            });
1960        }
1961        target.push(dep);
1962        Ok(())
1963    }
1964
1965    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
1966    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
1967    /// composite-reference accessor every consumer of the top-level
1968    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
1969    /// off — returns the author-declared `:limits` typed composite
1970    /// verbatim as an `Option<&LimitsSpec>` reference over the same
1971    /// backing storage the raw `self.limits.as_ref()` field access
1972    /// borrows from, with `None` naming the "no `:limits` block
1973    /// authored — every per-axis Lunatic-sandbox cap defers to the
1974    /// wasm-engine-default arm named on the per-axis
1975    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
1976    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
1977    /// docstrings" partition every downstream Servico-M2-overlay
1978    /// emitter treats as "emit nothing" and the sibling
1979    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
1980    /// treats as "skip the per-axis
1981    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
1982    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
1983    ///
1984    /// The outer `:limits` slot carries the M2 Servico-runtime typed
1985    /// composite — the load-bearing container of every Lunatic-shaped
1986    /// per-process wasm32-sandbox cap axis every long-running wasm
1987    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
1988    /// Lunatic per-process linear-memory / fuel / wall-clock /
1989    /// millicore cap primitives translated onto pleme-io's typed
1990    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
1991    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
1992    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
1993    /// chart both fan on). Every per-`:limits` axis threads through a
1994    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
1995    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
1996    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
1997    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
1998    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
1999    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2000    /// consumer that reaches for a limits axis first passes through
2001    /// this outer accessor onto the composite and then dispatches
2002    /// onto the per-axis accessor — the two-level dispatch means
2003    /// every per-`:limits` reader now routes through a typed dispatch
2004    /// on the substrate primitive at both altitudes.
2005    ///
2006    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2007    /// was accessed inline at three production sites — the
2008    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2009    /// `if let Some(l) = &caixa.limits { … }` traversal head
2010    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2011    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2012    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2013    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2014    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2015    /// [`LimitsSpec::validate`] fans onto), the
2016    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2017    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2018    /// head (caixa-core/src/render.rs:18504, which drives the
2019    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2020    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2021    /// block emitter fans on), and the
2022    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2023    /// set enumerator's `self.limits.is_some()` presence probe
2024    /// (caixa-core/src/manifest.rs:1788, which drives the
2025    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2026    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2027    /// gate reads) — three open-coded outer-field accesses that
2028    /// expressed no compile-time link back to the typed slot at the
2029    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2030    /// axis to a richer author surface (a multi-`:limits` list the M4
2031    /// CR materializer resolves per-CR at admission time so a Servico
2032    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2033    /// cluster `:limits-overrides` slot the operator pins so a
2034    /// cluster-specific policy can tighten a caixa-declared cap
2035    /// without re-authoring the `caixa.lisp`, a promotion of the
2036    /// plain `Option<LimitsSpec>` to a richer
2037    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2038    /// resolved dynamic-cap surface lands) would have had to be
2039    /// threaded through all three open-coded copies in lockstep or
2040    /// one consumer would silently disagree with the peers on which
2041    /// limits composite a given Caixa resolves to — the layout gate's
2042    /// per-axis bracket-dispatch seed reading the raw slot while the
2043    /// peer `servico_m2_overlay` emitter read an operator-resolved
2044    /// slot would silently split the build-time sandbox-shape gate
2045    /// from the runtime `ComputeUnit` CR emission gate, a three-
2046    /// consumer split at the layout gate, the M2 overlay emitter, and
2047    /// the declared-slot enumerator far from the source `caixa.lisp`
2048    /// with no field naming the limits-drift root cause. Lifting the
2049    /// resolution rule to a typed method on the substrate primitive
2050    /// means every downstream consumer of the caixa's per-`Caixa`
2051    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2052    /// one typed dispatch — the resolver's accept-set migrates as a
2053    /// unit on any future axis addition.
2054    ///
2055    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2056    /// composite-reference accessor — opens the outer-`Caixa`
2057    /// `Option<&Composite>` composite-reference projection pattern the
2058    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2059    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2060    /// [`crate::aplicacao::Placement`] / `:entrada`
2061    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2062    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2063    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2064    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2065    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2066    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2067    /// altitude — extends that "one typed dispatch on the substrate
2068    /// primitive, thin projections at each consumer" discipline onto
2069    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2070    /// runtime slot family's outer-composite axis. Returns
2071    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2072    /// clone) because every downstream consumer of the limits
2073    /// composite treats it as a read-only per-axis dispatch source —
2074    /// the reference-view is the narrowest borrow that supports every
2075    /// present + roadmapped consumer (per-axis accessor dispatch,
2076    /// `.is_empty()`-gated overlay projection, presence-probe early
2077    /// return on the "author-omitted `:limits` ⇒ engine-default
2078    /// applies" partition) without cloning the composite through
2079    /// every consumer's fast path. The `Option` half of the return-
2080    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2081    /// engine-default applies" partition (not a default composite the
2082    /// downstream must reject on emptiness) — the accessor projects
2083    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2084    /// reference-return unchanged. Named `limits()` to match the
2085    /// storage field's name verbatim and the tatara-lisp author-
2086    /// surface term (`:limits`) the field's own docstring already
2087    /// carries.
2088    #[must_use]
2089    pub fn limits(&self) -> Option<&LimitsSpec> {
2090        self.limits.as_ref()
2091    }
2092
2093    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2094    /// composite OTP-`gen_server`-shaped callback-table optional-
2095    /// composite-reference accessor every consumer of the top-level
2096    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2097    /// keys off — returns the author-declared `:behavior` typed
2098    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2099    /// the same backing storage the raw `self.behavior.as_ref()` field
2100    /// access borrows from, with `None` naming the "no `:behavior`
2101    /// block authored — every per-callback OTP-shaped hook defers to
2102    /// the wasm-engine's runtime default arm named on the per-axis
2103    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2104    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2105    /// [`BehaviorSpec::on_state_change`] /
2106    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2107    /// partition every downstream Servico-M2-overlay emitter treats as
2108    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2109    /// per-`:behavior` shape gate treats as "skip the per-arm
2110    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2111    /// per-callback on-disk `MissingEntry` existence check".
2112    ///
2113    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2114    /// composite — the load-bearing container of every OTP-shaped
2115    /// per-Servico lifecycle-callback path axis every long-running wasm
2116    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2117    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2118    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2119    /// translated onto pleme-io's typed `:behavior :on-init` /
2120    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2121    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2122    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2123    /// chart both fan on). Every per-`:behavior` axis threads through a
2124    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2125    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2126    /// Every downstream consumer that reaches for a behavior axis
2127    /// first passes through this outer accessor onto the composite
2128    /// and then dispatches onto the per-callback accessor — the
2129    /// two-level dispatch means every per-`:behavior` reader now
2130    /// routes through a typed dispatch on the substrate primitive at
2131    /// both altitudes.
2132    ///
2133    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2134    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2135    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2136    /// keys the "per-version `:state-change` instruction must have a
2137    /// `:on-state-change` callback" precondition off this accessor's
2138    /// composite (the callback-side counterpart to the
2139    /// `:upgrade-from :instructions :state-change :script` refusal at
2140    /// the appup-side). Threading that gate's traversal input through
2141    /// this accessor closes the cross-slot invariant on the substrate
2142    /// primitive, not on the raw field.
2143    ///
2144    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2145    /// composite was accessed inline at four production sites — the
2146    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2147    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2148    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2149    /// `BehaviorError` refusal cascade + the per-callback on-disk
2150    /// [`crate::LayoutError::MissingEntry`] existence check under
2151    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2152    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2153    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2154    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2155    /// drives the `:state-change` ↔ `:on-state-change` precondition
2156    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2157    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2158    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2159    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2160    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2161    /// Servico values-block emitter fans on), and the
2162    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2163    /// set enumerator's `self.behavior.is_some()` presence probe
2164    /// (caixa-core/src/manifest.rs:1919, which drives the
2165    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2166    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2167    /// gate reads) — four open-coded outer-field accesses that
2168    /// expressed no compile-time link back to the typed slot at the
2169    /// [`Caixa`] altitude. A future extension of the `:behavior`
2170    /// outer axis to a richer author surface (a per-callback overlay
2171    /// resolver the operator materializes at admission time so a
2172    /// cluster-specific policy can inject a per-callback tracing
2173    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2174    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2175    /// dynamic}` partition once a runtime-resolved behavior-swap
2176    /// surface lands, the M4 per-callback middleware chain the
2177    /// caixa-operator's per-Servico admission webhook keys off) would
2178    /// have had to be threaded through all four open-coded copies in
2179    /// lockstep or one consumer would silently disagree with the
2180    /// peers on which behavior composite a given Caixa resolves to —
2181    /// the layout gate's per-callback existence-check seed reading
2182    /// the raw slot while the peer `servico_m2_overlay` emitter read
2183    /// an operator-resolved slot would silently split the build-time
2184    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2185    /// gate from the cross-slot `:state-change` composition gate from
2186    /// the M2 declared-slot enumerator, a four-consumer split far
2187    /// from the source `caixa.lisp` with no field naming the
2188    /// behavior-drift root cause. Lifting the resolution rule to a
2189    /// typed method on the substrate primitive means every downstream
2190    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2191    /// composite surface reaches for exactly one typed dispatch — the
2192    /// resolver's accept-set migrates as a unit on any future axis
2193    /// addition.
2194    ///
2195    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2196    /// composite-reference accessor — sibling to the opening
2197    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2198    /// `Option<&Composite>` composite-reference sub-family, extends
2199    /// the "one typed dispatch on the substrate primitive, thin
2200    /// projections at each consumer" discipline onto the second of
2201    /// the three M2 Servico-runtime slots. The remaining
2202    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2203    /// altitude — the M3 mesh-slot family (`:politicas`,
2204    /// `:placement`, `:entrada` — already closed on the inner
2205    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2206    /// d32111c) — remain the future sibling lifts on the outer
2207    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2208    /// the owning composite by copy or clone) because every
2209    /// downstream consumer of the behavior composite treats it as a
2210    /// read-only per-callback dispatch source — the reference-view is
2211    /// the narrowest borrow that supports every present + roadmapped
2212    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2213    /// overlay projection, presence-probe early return on the
2214    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2215    /// partition, cross-slot `:state-change` composition input)
2216    /// without cloning the composite through every consumer's fast
2217    /// path. The `Option` half of the return-type preserves the
2218    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2219    /// applies" partition (not a default composite the downstream
2220    /// must reject on emptiness) — the accessor projects the raw
2221    /// `Option<BehaviorSpec>` slot's presence bit through the
2222    /// reference-return unchanged. Named `behavior()` to match the
2223    /// storage field's name verbatim and the tatara-lisp author-
2224    /// surface term (`:behavior`) the field's own docstring already
2225    /// carries.
2226    #[must_use]
2227    pub fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2228        self.behavior.as_ref()
2229    }
2230
2231    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2232    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2233    /// reference accessor every consumer of the top-level manifest's
2234    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2235    /// reader keys off — returns the author-declared `:politicas` typed
2236    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2237    /// same backing storage the raw `self.politicas.as_ref()` field
2238    /// access borrows from, with `None` naming the "no `:politicas`
2239    /// block authored — every per-axis mesh-policy scalar defers to the
2240    /// cluster-default arm named on the per-axis
2241    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2242    /// [`crate::aplicacao::MeshPolicy::retries`] /
2243    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2244    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2245    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2246    /// docstrings" partition every downstream caixa-mesh /
2247    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2248    /// "emit no per-`:politicas` overlay" and the sibling
2249    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2250    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2251    /// arm.
2252    ///
2253    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2254    /// Aplicacao typed composite — the load-bearing container of every
2255    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2256    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2257    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2258    /// composite; §V — the "no infinite blocking" per-call deadline +
2259    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2260    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2261    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2262    /// threads through a lifted per-slot accessor on the
2263    /// [`crate::aplicacao::MeshPolicy`] type: the
2264    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2265    /// mTLS-enforcement toggle, the
2266    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2267    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2268    /// (7073d0f) Gateway-API per-call deadline, the
2269    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2270    /// Envoy-outlier-detection composite. Every downstream consumer
2271    /// that reaches for a mesh-policy axis first passes through this
2272    /// outer accessor onto the composite and then dispatches onto the
2273    /// per-axis accessor — the two-level dispatch means every per-
2274    /// `:politicas` reader now routes through a typed dispatch on the
2275    /// substrate primitive at both altitudes.
2276    ///
2277    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2278    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2279    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2280    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2281    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2282    /// composite whether or not the author declared the outer slot.
2283    /// The outer accessor preserves the "author-omitted vs authored-
2284    /// empty" partition the inner accessor's `is_empty()`-gated
2285    /// renderer overlay collapses — routing the presence bit through
2286    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2287    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2288    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2289    ///
2290    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2291    /// composite was accessed inline at two production sites — the
2292    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2293    /// `self.politicas.clone().unwrap_or_default()` traversal head
2294    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2295    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2296    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2297    /// then observes), and the [`Self::declared_mesh_slots`] M3
2298    /// declared-slot-set enumerator's `self.politicas.is_some()`
2299    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2300    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2301    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2302    /// coherence gate reads) — two open-coded outer-field accesses
2303    /// that expressed no compile-time link back to the typed slot at
2304    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2305    /// outer axis to a richer author surface (a per-cluster
2306    /// `:politicas-overrides` slot the operator materializes at
2307    /// admission time so a cluster-specific policy can tighten the
2308    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2309    /// promotion of the plain `Option<MeshPolicy>` to a richer
2310    /// `{static, dynamic}` partition once the M4 per-edge
2311    /// contrato-scoped policy-override surface lands, the M5 traffic-
2312    /// shaping composition the caixa-operator's per-Aplicacao mesh
2313    /// admission webhook keys off) would have had to be threaded
2314    /// through both open-coded copies in lockstep or the Aplicacao-
2315    /// composition seed's default-fold arm would silently disagree
2316    /// with the M3 declared-slot enumerator on which policy composite
2317    /// a given Caixa resolves to — the seed reading an operator-
2318    /// resolved slot while the enumerator's presence probe read the
2319    /// raw slot would silently split the build-time mesh-artifact
2320    /// emission gate from the M3 declared-slot enumerator's kind-
2321    /// coherence gate, a two-consumer split far from the source
2322    /// `caixa.lisp` with no field naming the policy-drift root cause.
2323    /// Lifting the resolution rule to a typed method on the substrate
2324    /// primitive means every downstream consumer of the caixa's per-
2325    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2326    /// reaches for exactly one typed dispatch — the resolver's
2327    /// accept-set migrates as a unit on any future axis addition.
2328    ///
2329    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2330    /// composite-reference accessor — sibling to the opening
2331    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2332    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2333    /// reference sub-family, extends the "one typed dispatch on the
2334    /// substrate primitive, thin projections at each consumer"
2335    /// discipline onto the first of the three M3 mesh-slot axes.
2336    /// Peer of the closed inner mesh-slot outer-composite family the
2337    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2338    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2339    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2340    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2341    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2342    /// mesh-slot arm of the composite-reference family the remaining
2343    /// two axes (`:placement`, `:entrada`) fold onto in future
2344    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2345    /// composite by copy or clone) because every downstream consumer
2346    /// of the mesh-policy composite treats it as a read-only per-axis
2347    /// dispatch source — the reference-view is the narrowest borrow
2348    /// that supports every present + roadmapped consumer (per-axis
2349    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2350    /// presence-probe early return on the "author-omitted `:politicas`
2351    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2352    /// seed's default-fold arm) without cloning the composite through
2353    /// every consumer's fast path. The `Option` half of the return-
2354    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2355    /// cluster-default applies" partition (not a default composite
2356    /// the downstream must reject on emptiness) — the accessor
2357    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2358    /// through the reference-return unchanged. Named `politicas()` to
2359    /// match the storage field's name verbatim and the tatara-lisp
2360    /// author-surface term (`:politicas`) the field's own docstring
2361    /// already carries.
2362    #[must_use]
2363    pub fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2364        self.politicas.as_ref()
2365    }
2366
2367    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2368    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2369    /// reference accessor every consumer of the top-level manifest's
2370    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2371    /// reader keys off — returns the author-declared `:placement` typed
2372    /// composite verbatim as an `Option<&Placement>` reference over the
2373    /// same backing storage the raw `self.placement.as_ref()` field
2374    /// access borrows from, with `None` naming the "no `:placement`
2375    /// block authored — every per-axis placement scalar defers to the
2376    /// cluster-default arm named on the per-axis
2377    /// [`crate::aplicacao::Placement::estrategia`] /
2378    /// [`crate::aplicacao::Placement::clusters`] /
2379    /// [`crate::aplicacao::Placement::affinity`] /
2380    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2381    /// docstrings" partition every downstream caixa-mesh /
2382    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2383    /// "emit no per-`:placement` overlay" and the sibling
2384    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2385    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2386    ///
2387    /// The outer `:placement` slot carries the M3 mesh-slot per-
2388    /// Aplicacao typed distribution composite — the load-bearing
2389    /// container of every where-does-this-Aplicacao-run axis every
2390    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2391    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2392    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2393    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2394    /// Aplicacao's typed distribution composite; §V CSE invariants —
2395    /// "distribution is a first-class typed composite, not a runtime
2396    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2397    /// typed inter-Servico contrato-edge overlay the per-cluster
2398    /// mesh renderer keys off). Every per-`:placement` axis threads
2399    /// through a lifted per-slot accessor on the
2400    /// [`crate::aplicacao::Placement`] type: the
2401    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2402    /// MESH-COMPOSITION distribution-strategy scalar, the
2403    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2404    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2405    /// M3-Adaptive-compression-hint optional-scalar, and the
2406    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2407    /// sharding extractor-expression optional-scalar. Every downstream
2408    /// consumer that reaches for a placement axis first passes through
2409    /// this outer accessor onto the composite and then dispatches onto
2410    /// the per-axis accessor — the two-level dispatch means every per-
2411    /// `:placement` reader now routes through a typed dispatch on the
2412    /// substrate primitive at both altitudes.
2413    ///
2414    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2415    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2416    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2417    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2418    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2419    /// whether or not the author declared the outer slot. The outer
2420    /// accessor preserves the "author-omitted vs authored-empty" partition
2421    /// the inner accessor collapses at the cluster-default fold —
2422    /// routing the presence bit through this accessor keeps the
2423    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2424    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2425    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2426    /// dispatch.
2427    ///
2428    /// Prior to this lift the `.placement` `Option<Placement>`
2429    /// composite was accessed inline at two production sites — the
2430    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2431    /// `self.placement.clone().unwrap_or_default()` traversal head
2432    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2433    /// the [`crate::aplicacao::Placement::default`] cluster-default
2434    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2435    /// then observes), and the [`Self::declared_mesh_slots`] M3
2436    /// declared-slot-set enumerator's `self.placement.is_some()`
2437    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2438    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2439    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2440    /// coherence gate reads) — two open-coded outer-field accesses
2441    /// that expressed no compile-time link back to the typed slot at
2442    /// the [`Caixa`] altitude. A future extension of the `:placement`
2443    /// outer axis to a richer author surface (a per-cluster
2444    /// `:placement-overrides` slot the operator materializes at
2445    /// admission time so a cluster-specific placement can tighten the
2446    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2447    /// per-tenant placement-alias table the M4
2448    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2449    /// per-CR at admission time, a promotion of the plain
2450    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2451    /// once Orleans-style virtual-actor dynamic placement comes into
2452    /// typed scope) would have had to be threaded through both open-
2453    /// coded copies in lockstep or the Aplicacao-composition seed's
2454    /// default-fold arm would silently disagree with the M3 declared-
2455    /// slot enumerator on which distribution composite a given Caixa
2456    /// resolves to — the seed reading an operator-resolved slot while
2457    /// the enumerator's presence probe read the raw slot would
2458    /// silently split the build-time distribution-artifact emission
2459    /// gate from the M3 declared-slot enumerator's kind-coherence
2460    /// gate, a two-consumer split far from the source `caixa.lisp`
2461    /// with no field naming the distribution-drift root cause.
2462    /// Lifting the resolution rule to a typed method on the substrate
2463    /// primitive means every downstream consumer of the caixa's per-
2464    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2465    /// reaches for exactly one typed dispatch — the resolver's
2466    /// accept-set migrates as a unit on any future axis addition.
2467    ///
2468    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2469    /// composite-reference accessor — sibling to the opening
2470    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2471    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2472    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2473    /// composite-reference sub-family, folds on the "one typed
2474    /// dispatch on the substrate primitive, thin projections at each
2475    /// consumer" discipline extended onto the second of the three M3
2476    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2477    /// composite family the sibling
2478    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2479    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2480    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2481    /// accessor pins already close on the inner
2482    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2483    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2484    /// [`Self::politicas`] opened, extending the discipline onto the
2485    /// second of the three M3 mesh-slot axes. The remaining M3
2486    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2487    /// discipline in the final sibling lift, closing the outer top-
2488    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2489    /// Returns `Option<&Placement>` (not the owning composite by copy
2490    /// or clone) because every downstream consumer of the placement
2491    /// composite treats it as a read-only per-axis dispatch source —
2492    /// the reference-view is the narrowest borrow that supports every
2493    /// present + roadmapped consumer (per-axis accessor dispatch,
2494    /// serde composite-serialization on the programs.yaml overlay,
2495    /// presence-probe early return on the "author-omitted `:placement`
2496    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2497    /// seed's default-fold arm) without cloning the composite through
2498    /// every consumer's fast path. The `Option` half of the return-
2499    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2500    /// cluster-default applies" partition (not a default composite
2501    /// the downstream must reject on emptiness) — the accessor
2502    /// projects the raw `Option<Placement>` slot's presence bit
2503    /// through the reference-return unchanged. Named `placement()` to
2504    /// match the storage field's name verbatim and the tatara-lisp
2505    /// author-surface term (`:placement`) the field's own docstring
2506    /// already carries.
2507    #[must_use]
2508    pub fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2509        self.placement.as_ref()
2510    }
2511
2512    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2513    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2514    /// composite-reference accessor every consumer of the top-level
2515    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2516    /// composite reader keys off — returns the author-declared
2517    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2518    /// reference over the same backing storage the raw
2519    /// `self.entrada.as_ref()` field access borrows from, with `None`
2520    /// naming the "no `:entrada` block authored — this Aplicacao is
2521    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2522    /// partition every downstream caixa-mesh Gateway-API artifact
2523    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2524    /// backend for this Aplicacao" and the sibling
2525    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2526    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2527    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2528    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2529    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2530    /// the same `Option<&Entrada>` presence bit unchanged).
2531    ///
2532    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2533    /// Aplicacao typed external-gateway composite — the load-bearing
2534    /// container of every how-does-the-outside-world-reach-this-
2535    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2536    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2537    /// external-entry composite; §V CSE invariants — "the external
2538    /// gateway is a first-class typed composite, not a per-Servico
2539    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2540    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2541    /// API renderer keys off). Every per-`:entrada` axis threads
2542    /// through a lifted per-slot accessor on the
2543    /// [`crate::aplicacao::Entrada`] type: the
2544    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2545    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2546    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2547    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2548    /// backend `trigger.service.port` scalar, and the
2549    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2550    /// resolver every HTTPRoute-aware renderer consumes. Every
2551    /// downstream consumer that reaches for an entry axis first passes
2552    /// through this outer accessor onto the composite and then
2553    /// dispatches onto the per-axis accessor — the two-level dispatch
2554    /// means every per-`:entrada` reader now routes through a typed
2555    /// dispatch on the substrate primitive at both altitudes.
2556    ///
2557    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2558    /// seed: the Aplicacao-view builder forwards the outer `Option`
2559    /// arm verbatim (no default fold — `:entrada` is inherently
2560    /// optional; a cluster-internal Aplicacao has no external gateway
2561    /// at all, not "an external gateway that defaults to nothing"), so
2562    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2563    /// `Option<&Entrada>`-return accessor observes the same presence
2564    /// bit whether or not the author declared the outer slot. Routing
2565    /// the presence bit through this accessor keeps the
2566    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2567    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2568    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2569    /// hostname/backend/path emission dispatch.
2570    ///
2571    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2572    /// was accessed inline at two production sites — the
2573    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2574    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2575    /// which drives the forward onto the peer inner
2576    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2577    /// Gateway-API fan-out then observes), and the
2578    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2579    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2580    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2581    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2582    /// kind-coherence gate reads) — two open-coded outer-field
2583    /// accesses that expressed no compile-time link back to the typed
2584    /// slot at the [`Caixa`] altitude. A future extension of the
2585    /// `:entrada` outer axis to a richer author surface (a per-cluster
2586    /// `:entrada-overrides` slot the operator materializes at admission
2587    /// time so a cluster-specific hostname can pin the caixa-declared
2588    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2589    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2590    /// CR materializer resolves per-CR at admission time, a promotion
2591    /// of the plain `Option<Entrada>` to a richer
2592    /// `{public, private, internal}` partition once Cilium-identity-
2593    /// scoped internal gateways come into typed scope) would have had
2594    /// to be threaded through both open-coded copies in lockstep or the
2595    /// Aplicacao-composition seed's forward arm would silently
2596    /// disagree with the M3 declared-slot enumerator on which external-
2597    /// gateway composite a given Caixa resolves to — the seed reading
2598    /// an operator-resolved slot while the enumerator's presence probe
2599    /// read the raw slot would silently split the build-time gateway-
2600    /// artifact emission gate from the M3 declared-slot enumerator's
2601    /// kind-coherence gate, a two-consumer split far from the source
2602    /// `caixa.lisp` with no field naming the entry-drift root cause.
2603    /// Lifting the resolution rule to a typed method on the substrate
2604    /// primitive means every downstream consumer of the caixa's per-
2605    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2606    /// surface reaches for exactly one typed dispatch — the resolver's
2607    /// accept-set migrates as a unit on any future axis addition.
2608    ///
2609    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2610    /// return composite-reference accessor — closes the outer-`Caixa`
2611    /// `Option<&Composite>` composite-reference sub-family opened by
2612    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2613    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2614    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2615    /// folds on the "one typed dispatch on the substrate primitive,
2616    /// thin projections at each consumer" discipline extended onto the
2617    /// third and final M3 mesh-slot axis. Peer of the closed inner
2618    /// mesh-slot outer-composite family the sibling
2619    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2620    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2621    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2622    /// accessor pins already close on the inner
2623    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2624    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2625    /// altitudes of the outer-composite reference-return discipline
2626    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2627    /// slot presence) now carry the full five-arm accept-set behind a
2628    /// typed dispatch on the substrate primitive. Returns
2629    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2630    /// because every downstream consumer of the entrada composite
2631    /// treats it as a read-only per-axis dispatch source — the
2632    /// reference-view is the narrowest borrow that supports every
2633    /// present + roadmapped consumer (per-axis accessor dispatch,
2634    /// serde composite-serialization on the programs.yaml overlay,
2635    /// presence-probe early return on the "author-omitted `:entrada`
2636    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2637    /// seed's forward arm) without cloning the composite through every
2638    /// consumer's fast path. The `Option` half of the return-type
2639    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2640    /// cluster-internal Aplicacao" partition (not a default composite
2641    /// the downstream must reject on emptiness — a cluster-internal
2642    /// Aplicacao has no external gateway at all, not "a default gateway
2643    /// that emits nothing"); the accessor projects the raw
2644    /// `Option<Entrada>` slot's presence bit through the reference-
2645    /// return unchanged. Named `entrada()` to match the storage field's
2646    /// name verbatim and the tatara-lisp author-surface term
2647    /// (`:entrada`) the field's own docstring already carries.
2648    #[must_use]
2649    pub fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
2650        self.entrada.as_ref()
2651    }
2652
2653    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
2654    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
2655    /// an `Option<&CiRun>`, borrowed from the typed slot's own
2656    /// `Option<CiRun>` storage. `None` when the slot is absent (every
2657    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
2658    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
2659    /// not silently accepted).
2660    ///
2661    /// Named `ci()` to match the storage field's name and the
2662    /// tatara-lisp author surface (`:ci`); mirrors the sibling
2663    /// `Option<&Composite>` accessors on this same `Caixa` altitude
2664    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
2665    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
2666    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
2667    /// at every consumer.
2668    #[must_use]
2669    pub fn ci(&self) -> Option<&canteiro_types::CiRun> {
2670        self.ci.as_ref()
2671    }
2672
2673    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
2674    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
2675    /// accessor every consumer of the top-level manifest's per-Supervisor
2676    /// restart-strategy axis keys off — returns the author-declared
2677    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
2678    /// `Copy`-projected from the typed slot's own
2679    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
2680    /// (`:estrategia` is a flat-spread supervisor-only slot every
2681    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
2682    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2683    /// still omit to defer to [`RestartStrategy::default`] —
2684    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
2685    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
2686    /// [`SupervisorSpec::default`]-inherited strategy without any silent
2687    /// promotion to a fresh explicit variant at the accessor boundary).
2688    ///
2689    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
2690    /// restart-strategy discriminant every substrate-side per-Supervisor
2691    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
2692    /// closed-set `one_for_one | one_for_all | rest_for_one |
2693    /// simple_one_for_one` algebra translated onto pleme-io's typed
2694    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
2695    /// slot algebra the operator's hierarchical reconciliation scheduler
2696    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
2697    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
2698    /// supervisor slots are flat on Caixa (vs nested under a
2699    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
2700    /// level of nesting"), so the accessor's altitude is the outer
2701    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
2702    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
2703    /// (eafb619) accessor keys off. The two typed axes — the outer
2704    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
2705    /// (author-omitted arm carried as `None`) and the inner post-
2706    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
2707    /// (`Option` collapsed through the [`Self::supervisor_view`]
2708    /// `unwrap_or_default()` fold) — now share one accessor discipline for
2709    /// the shared substrate concept "the author-declared OTP-shaped
2710    /// sibling-restart-strategy variant that partitions the downstream
2711    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
2712    /// `None` arm is the pre-composition presence bit every declared-slot
2713    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
2714    /// inner-altitude non-`Option` `RestartStrategy` is the post-
2715    /// composition partition-dispatch input every strategy-arm consumer
2716    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
2717    /// Supervisor sibling-restart branch, the future M4
2718    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
2719    /// webhook) fans on.
2720    ///
2721    /// Prior to this lift the `.estrategia` field was accessed inline at
2722    /// two production sites in `caixa-core/src/manifest.rs` — the
2723    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
2724    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
2725    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2726    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
2727    /// `SupervisorSpec` construction site at `estrategia:
2728    /// self.estrategia.unwrap_or_default()` (which composes the flat-
2729    /// spread outer author-surface `Option<RestartStrategy>` onto the
2730    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
2731    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
2732    /// coded field-accesses that expressed no compile-time link back to
2733    /// the typed slot. A future extension of the outer `:estrategia` axis
2734    /// to a richer author surface (a per-cluster strategy override the
2735    /// operator pins through a future `:estrategia-overrides` overlay the
2736    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
2737    /// a per-tenant strategy-alias table the M4 CR materializer resolves
2738    /// per-CR, a per-Supervisor dynamic strategy derivation the future
2739    /// adaptive-supervision engine computes from child-failure-history
2740    /// topology, a per-child-cohort strategy split the future
2741    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
2742    /// absorption roadmap acknowledges, a promotion of the plain
2743    /// `Option<RestartStrategy>` to a richer
2744    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
2745    /// operator-resolved overlay lands) would have had to be threaded
2746    /// through both open-coded copies in lockstep or the enumerator's
2747    /// presence probe and the composition site's `unwrap_or_default()`
2748    /// fold would silently disagree on which strategy a given [`Caixa`]
2749    /// resolves to (an author's `:estrategia OneForAll` would satisfy
2750    /// the enumerator's presence probe while the composition site
2751    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
2752    /// the resolution rule to a typed method on the substrate primitive
2753    /// means every downstream consumer of the caixa's per-`Caixa` outer-
2754    /// altitude sibling-restart-strategy surface reaches for exactly one
2755    /// typed dispatch — the resolver's accept-set migrates as a unit on
2756    /// any future axis addition.
2757    ///
2758    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2759    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2760    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
2761    /// projection pattern the sibling per-`Caixa` `:max-restarts`
2762    /// `Option<u32>` and (through the future duration-newtype landing)
2763    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
2764    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
2765    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
2766    /// the post-composition [`SupervisorSpec`] altitude — same "one
2767    /// typed dispatch on the substrate primitive, thin projections at
2768    /// each consumer" discipline extended onto the pre-composition outer
2769    /// author-surface [`Caixa`] altitude for the same OTP-shaped
2770    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
2771    /// `Option<&Composite>` composite-reference family the sibling
2772    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
2773    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
2774    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
2775    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
2776    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
2777    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
2778    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2779    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
2780    /// pins on the inner-altitude per-`:placement` composite. Named
2781    /// `estrategia()` to match the storage field's name and the
2782    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
2783    /// / per-[`crate::aplicacao::Placement`] peer
2784    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
2785    /// verbatim; the accessor's identity name maps onto the canonical
2786    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
2787    /// docstring already carries.
2788    #[must_use]
2789    pub fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
2790        self.estrategia
2791    }
2792
2793    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
2794    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
2795    /// scalar accessor every consumer of the top-level manifest's per-
2796    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
2797    /// returns the author-declared `:max-restarts` typed `Option<u32>`
2798    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
2799    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
2800    /// accessor returns by value; no borrow of `&self` past the call).
2801    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
2802    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
2803    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
2804    /// still omit to defer to the [`Self::supervisor_view`]
2805    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
2806    ///
2807    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
2808    /// `MaxIntensity` restart-budget count that pairs with the sibling
2809    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
2810    /// restart-intensity ratio the supervisor trips its own escalation on
2811    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
2812    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
2813    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
2814    /// reconciliation scheduler fans on). The slot is *flat-spread* on
2815    /// the outer top-level `Caixa` (per the field-shape docstring at
2816    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
2817    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
2818    /// accessor's altitude is the outer [`Caixa`] surface rather than the
2819    /// composed [`SupervisorSpec`] altitude the sibling
2820    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
2821    /// off. The two typed axes — the outer author-surface `Option<u32>`
2822    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
2823    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
2824    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
2825    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
2826    /// shared substrate concept "the author-declared OTP-shaped
2827    /// restart-budget count every downstream per-Supervisor consumer's
2828    /// restart-intensity budget-vs-count comparator fans on".
2829    ///
2830    /// Prior to this lift the `.max_restarts` field was accessed inline
2831    /// at two production sites in `caixa-core/src/manifest.rs` — the
2832    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
2833    /// presence-probe arm at `if self.max_restarts.is_some()` (which
2834    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
2835    /// kind-coherence gate's per-slot label push) and the
2836    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
2837    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
2838    /// flat-spread outer author-surface `Option<u32>` onto the inner
2839    /// post-composition [`SupervisorSpec`] `u32` field the
2840    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
2841    /// coded field-accesses that expressed no compile-time link back to
2842    /// the typed slot. A future extension of the outer `:max-restarts`
2843    /// axis to a richer author surface (a per-cluster restart-budget
2844    /// override the operator pins through a future `:max-restarts-overrides`
2845    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
2846    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
2847    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
2848    /// budget derivation the future adaptive-supervision engine computes
2849    /// from child-failure-history topology, a promotion of the plain
2850    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
2851    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
2852    /// per-child-cohort roadmap lands) would have had to be threaded
2853    /// through both open-coded copies in lockstep or the enumerator's
2854    /// presence probe and the composition site's `unwrap_or(5)` fold
2855    /// would silently disagree on which restart-budget a given [`Caixa`]
2856    /// resolves to (an author's `:max-restarts 10` would satisfy the
2857    /// enumerator's presence probe while the composition site silently
2858    /// composed the OTP-canonical `5`, or vice versa). Lifting the
2859    /// resolution rule to a typed method on the substrate primitive means
2860    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
2861    /// restart-budget-count surface reaches for exactly one typed dispatch
2862    /// — the resolver's accept-set migrates as a unit on any future axis
2863    /// addition.
2864    ///
2865    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
2866    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
2867    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
2868    /// projection pattern the sibling per-`Caixa`
2869    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
2870    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
2871    /// Peer of the inner-altitude
2872    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
2873    /// on the post-composition [`SupervisorSpec`] altitude — same "one
2874    /// typed dispatch on the substrate primitive, thin projections at
2875    /// each consumer" discipline extended onto the pre-composition outer
2876    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
2877    /// shaped restart-budget-count axis. Named `max_restarts()` to match
2878    /// the storage field's name and the per-[`SupervisorSpec`] peer
2879    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
2880    /// discipline verbatim; the accessor's identity maps onto the
2881    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
2882    /// field's docstring already carries.
2883    #[must_use]
2884    pub const fn max_restarts(&self) -> Option<u32> {
2885        self.max_restarts
2886    }
2887
2888    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
2889    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
2890    /// denominator raw-duration-string scalar accessor every consumer of
2891    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
2892    /// window axis keys off — returns the author-declared `:restart-window`
2893    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
2894    /// from the typed slot's own `Option<String>` storage. `None` when
2895    /// the slot is absent (the canonical "never reset — every restart
2896    /// across the supervisor's lifetime counts against the sibling
2897    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
2898    /// `defcaixa` carries by `#[serde(default)]` and every
2899    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
2900    /// [`Self::supervisor_view`] `restart_window: None` composition
2901    /// through the [`crate::supervisor::duration_codec::parse`] soft-
2902    /// swallow `.and_then(|s| … .ok())` fold).
2903    ///
2904    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
2905    /// shaped `Period` sliding-observation-interval duration string that
2906    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
2907    /// budget count to form the `MaxIntensity / Period` restart-intensity
2908    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
2909    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
2910    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
2911    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
2912    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
2913    /// holds an `Option<Duration>` routed through the shared
2914    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
2915    /// — so the outer altitude's accessor returns `Option<&str>` (raw
2916    /// authoring surface) while the inner altitude's
2917    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
2918    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
2919    /// is closed by the sibling [`Self::validate_restart_window`] gate
2920    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
2921    /// the offending value; the view-construction path
2922    /// [`Self::supervisor_view`] soft-swallows the same parse error to
2923    /// `None` to keep the view best-effort.
2924    ///
2925    /// Prior to this lift the `.restart_window` field was accessed inline
2926    /// at three production sites in `caixa-core/src/manifest.rs` — the
2927    /// [`Self::declared_supervisor_slots`]
2928    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
2929    /// `if self.restart_window.is_some()` (which drives the
2930    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
2931    /// coherence gate's per-slot label push), the
2932    /// [`Self::validate_restart_window`] `let Some(s) =
2933    /// self.restart_window.as_deref()` empty-and-shape gate binding
2934    /// (which folds the raw string through the shared
2935    /// [`crate::supervisor::duration_codec::parse`] to surface
2936    /// [`ManifestError::RestartWindowMalformed`] naming the offending
2937    /// value), and the [`Self::supervisor_view`] `self.restart_window
2938    /// .as_deref().and_then(…)` view-construction fold (which composes
2939    /// the flat-spread outer author-surface `Option<String>` onto the
2940    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
2941    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
2942    /// three open-coded field-accesses that expressed no compile-time
2943    /// link back to the typed slot. A future extension of the outer
2944    /// `:restart-window` axis to a richer author surface (a per-cluster
2945    /// window override, a per-tenant window-alias table, a per-Supervisor
2946    /// dynamic window derivation the future adaptive-supervision engine
2947    /// computes from child-failure-history topology, a promotion of the
2948    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
2949    /// once the future author-surface parser lands at the [`Caixa`]
2950    /// altitude and the raw-string form is retired) would have had to be
2951    /// threaded through every open-coded copy in lockstep or the three
2952    /// consumers would silently disagree on which raw string a given
2953    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
2954    /// method on the substrate primitive means every downstream consumer
2955    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
2956    /// string surface reaches for exactly one typed dispatch — the
2957    /// resolver's accept-set migrates as a unit on any future axis
2958    /// addition.
2959    ///
2960    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
2961    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
2962    /// spread projection pattern the sibling per-`Caixa`
2963    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
2964    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
2965    /// the sub-family onto the sibling `Option<&str>` raw-duration-
2966    /// string arm (the outer altitude's raw-string form; the inner
2967    /// altitude's parsed [`Duration`] form is the peer
2968    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
2969    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
2970    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
2971    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
2972    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
2973    /// sub-family already carries — same "one typed dispatch on the
2974    /// substrate primitive, thin projections at each consumer"
2975    /// discipline extended onto the M2 supervisor-tree flat-spread
2976    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
2977    /// to match the storage field's name and the per-[`SupervisorSpec`]
2978    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
2979    /// method-name discipline verbatim; the accessor's identity maps
2980    /// onto the canonical OTP-shape supervision vocabulary the
2981    /// `:restart-window` field's docstring already carries.
2982    #[must_use]
2983    pub fn restart_window(&self) -> Option<&str> {
2984        self.restart_window.as_deref()
2985    }
2986
2987    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
2988    /// outer-composite OTP-appup-shaped per-prior-version migration-
2989    /// entry-list slice accessor every consumer of the top-level
2990    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
2991    /// slice-view keys off — returns the author-declared `:upgrade-from`
2992    /// typed `Vec<UpgradeFromEntry>` verbatim as a
2993    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
2994    /// the raw `self.upgrade_from.as_slice()` field access borrows
2995    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
2996    /// arm every `defcaixa` without an `:upgrade-from` block carries;
2997    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
2998    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
2999    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3000    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3001    /// degenerates to an empty slice on that arm without any silent
3002    /// `None` collapse).
3003    ///
3004    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3005    /// migration block — the load-bearing container of every per-
3006    /// prior-`:versao` migration-instruction list the wasm-operator
3007    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3008    /// `.appup` per-prior-version `LoadModule | StateChange |
3009    /// SoftPurge | Purge | Restart` instruction algebra translated
3010    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3011    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3012    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3013    /// threads through a lifted per-entry accessor on the
3014    /// [`UpgradeFromEntry`] type: the
3015    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3016    /// version scalar accessor and the
3017    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3018    /// return per-entry instruction-list accessor (0137e5a). Every
3019    /// downstream consumer of the hot-upgrade path first passes
3020    /// through this outer accessor onto the slice and then dispatches
3021    /// per-entry through the inner accessors — the two-level dispatch
3022    /// means every per-`:upgrade-from` reader now routes through a
3023    /// typed dispatch on the substrate primitive at both altitudes.
3024    ///
3025    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3026    /// slot was accessed inline at production sites across three
3027    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3028    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3029    /// (caixa-core/src/manifest.rs, which drives the
3030    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3031    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3032    /// gate reads), the [`crate::StandardLayout::verify`] per-
3033    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3034    /// layout.rs, which fans onto the
3035    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3036    /// cross-entry duplicate gate, the
3037    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3038    /// SemVer-precedence cross-slot gate, the
3039    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3040    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3041    /// gate, and the per-instruction script-path existence-probe walk
3042    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3043    /// resolve every declared migration script against the layout
3044    /// root), and the [`crate::render::servico_m2_overlay`] per-
3045    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3046    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3047    /// projection (caixa-core/src/render.rs, which drives the
3048    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3049    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3050    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3051    /// A future extension of the outer `:upgrade-from` axis (a per-
3052    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3053    /// resolves at admission time so a cluster-specific migration
3054    /// policy can tighten a caixa-declared step without re-authoring
3055    /// the `caixa.lisp`, promotion of the plain
3056    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3057    /// partition once runtime-resolved hot-upgrade instructions land,
3058    /// per-entry priority annotation once multi-strategy fan-out
3059    /// lands) would have had to be threaded through all six open-
3060    /// coded copies in lockstep or one consumer would silently
3061    /// disagree with the peers on which upgrade slice a given Caixa
3062    /// resolves to — a six-consumer split at the enumerator, the
3063    /// three-stage validate pass, the script-path probe walk, and the
3064    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3065    /// field naming the upgrade-drift root cause. Lifting the
3066    /// resolution rule to a typed method on the substrate primitive
3067    /// means every downstream consumer of the caixa's per-`Caixa`
3068    /// OTP-appup outer-slice surface reaches for exactly one typed
3069    /// dispatch — the resolver's accept-set migrates as a unit on any
3070    /// future axis addition.
3071    ///
3072    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3073    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3074    /// outer-`Caixa` `&[Composite]` composite-slice projection
3075    /// pattern the sibling `:children`
3076    /// [`crate::supervisor::ChildSpec`] / `:membros`
3077    /// [`crate::aplicacao::Membro`] / `:contratos`
3078    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3079    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3080    /// `Option<&Composite>` composite-reference family the sibling
3081    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3082    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3083    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3084    /// `Option<&Composite>` altitude, extended here to the outer-
3085    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3086    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3087    /// (0137e5a) — same "one typed dispatch on the substrate
3088    /// primitive, thin projections at each consumer" discipline
3089    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3090    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3091    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3092    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3093    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3094    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3095    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3096    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3097    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3098    /// slice" projection pattern onto the sibling M2 typed-composite-
3099    /// element axis (`UpgradeFromEntry` composite, matching the
3100    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3101    /// different altitude).
3102    ///
3103    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3104    /// because every downstream consumer of the hot-upgrade list
3105    /// treats it as a read-only sequence — the slice-view is the
3106    /// narrowest borrow that supports every present + roadmapped
3107    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3108    /// serialization through
3109    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3110    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3111    /// the typed view reaches for (the storage-side `Vec` remains
3112    /// reachable through the `pub upgrade_from` field for the
3113    /// mutation-carrying serde round-trip and per-test fixture-
3114    /// mutation paths). Named `upgrade_from()` to match the storage
3115    /// field's `snake_case` name; the kebab-case author-surface tag
3116    /// `:upgrade-from` is the same axis after tatara-lisp's
3117    /// kebab↔snake fold and the accessor's identity maps onto the
3118    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3119    /// already carries.
3120    #[must_use]
3121    pub fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3122        self.upgrade_from.as_slice()
3123    }
3124
3125    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3126    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3127    /// slice accessor every consumer of the top-level manifest's per-
3128    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3129    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3130    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3131    /// the same backing buffer the raw `self.children.as_slice()` field
3132    /// access borrows from. Empty-slice-carrying (the "no static children
3133    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3134    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3135    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3136    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3137    /// on those arms without any silent `None` collapse).
3138    ///
3139    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3140    /// static-child list — the load-bearing container of every per-
3141    /// child `{caixa, versao, restart}` triple the wasm-operator's
3142    /// hierarchical reconciler dispatches on at supervisor-tree
3143    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3144    /// static-child list translated onto pleme-io's typed
3145    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3146    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3147    /// dispatch fans on). Every per-child axis threads through a lifted
3148    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3149    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3150    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3151    /// version-requirement scalar accessor, and the
3152    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3153    /// per-child post-exit restart-decision-policy discriminant
3154    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3155    /// tree path first passes through this outer accessor onto the
3156    /// slice and then dispatches per-child through the inner accessors
3157    /// — the two-level dispatch means every per-`:children` reader now
3158    /// routes through a typed dispatch on the substrate primitive at
3159    /// both altitudes.
3160    ///
3161    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3162    /// accessed inline at three production sites across two files —
3163    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3164    /// declared-slot enumerator's `!self.children.is_empty()` presence
3165    /// probe (caixa-core/src/manifest.rs, which drives the
3166    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3167    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3168    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3169    /// per-supervisor typed-view composer's `self.children.clone()`
3170    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3171    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3172    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3173    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3174    /// `:children :caixa` self-parent refusal probe's
3175    /// `&caixa.children`-borrowed
3176    /// [`crate::supervisor::validate_no_self_supervision`] input
3177    /// (caixa-core/src/layout.rs, which pins the "no child names the
3178    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3179    /// extension of the outer `:children` axis (a per-cluster
3180    /// `:children-overrides` overlay the wasm-engine operator resolves
3181    /// at admission time so a cluster-specific child-set can tighten
3182    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3183    /// promotion of the plain `Vec<ChildSpec>` to a richer
3184    /// `{static, dynamic}` partition once Erlang/OTP's
3185    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3186    /// axis, per-child priority annotation once multi-strategy fan-out
3187    /// lands) would have had to be threaded through all three open-
3188    /// coded copies in lockstep or one consumer would silently
3189    /// disagree with the peers on which child slice a given Caixa
3190    /// resolves to — the enumerator's presence probe reading the raw
3191    /// slot while the peer view-composer's fold-in path read an
3192    /// operator-resolved slot would silently split the paired
3193    /// declared-slot enumerator and typed-view composition, and the
3194    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3195    /// refusal probe reading a third borrow would silently drift the
3196    /// cross-slot coherence gate's traversal input from the two peers,
3197    /// a three-consumer split at the enumerator, the view composer,
3198    /// and the self-parent gate far from the source `caixa.lisp` with
3199    /// no field naming the child-set-drift root cause. Lifting the
3200    /// resolution rule to a typed method on the substrate primitive
3201    /// means every downstream consumer of the caixa's per-`Caixa`
3202    /// OTP-supervisor outer-slice surface reaches for exactly one
3203    /// typed dispatch — the resolver's accept-set migrates as a unit
3204    /// on any future axis addition.
3205    ///
3206    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3207    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3208    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3209    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3210    /// at the outer altitude of the closed inner-`SupervisorSpec`
3211    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3212    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3213    /// borrow-shared" outer-accessor discipline extended onto the
3214    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3215    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3216    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3217    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3218    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3219    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3220    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3221    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3222    /// M2 typed-composite-element axis
3223    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3224    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3225    /// different altitude).
3226    ///
3227    /// Returns `&[crate::supervisor::ChildSpec]` (not
3228    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3229    /// child list treats it as a read-only sequence — the slice-view
3230    /// is the narrowest borrow that supports every present +
3231    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3232    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3233    /// input, `serde` slice-serialization) without leaking the backing
3234    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3235    /// reaches for (the storage-side `Vec` remains reachable through
3236    /// the `pub children` field for the mutation-carrying serde round-
3237    /// trip and per-test fixture-mutation paths, including the
3238    /// [`Self::supervisor_view`] fold-in path that clones the slot
3239    /// into the typed view). Named `children()` to match the storage
3240    /// field's name verbatim and the tatara-lisp author-surface term
3241    /// (`:children`) the field's own docstring already carries; the
3242    /// accessor's identity maps onto the canonical OTP supervision
3243    /// vocabulary the [`Caixa::children`] field's docstring already
3244    /// reaches for ("Static children of a supervisor").
3245    #[must_use]
3246    pub fn children(&self) -> &[crate::supervisor::ChildSpec] {
3247        self.children.as_slice()
3248    }
3249
3250    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3251    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3252    /// accessor every consumer of the top-level manifest's per-Aplicacao
3253    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3254    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3255    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3256    /// same backing buffer the raw `self.membros.as_slice()` field access
3257    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3258    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3259    /// and every partially-authored Aplicacao carries before the
3260    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3261    /// `&[Membro]` degenerates to an empty slice on those arms without any
3262    /// silent `None` collapse).
3263    ///
3264    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3265    /// per-Aplicacao member list — the load-bearing container of every
3266    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3267    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3268    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3269    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3270    /// the `:entrada :para` external-gateway destination validates
3271    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3272    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3273    /// threads through a lifted per-entry accessor on the
3274    /// [`crate::aplicacao::Membro`] type: the
3275    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3276    /// identity scalar accessor (4a32abf) and the peer
3277    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3278    /// version-requirement scalar accessor (a40b0e3). Every downstream
3279    /// consumer of the mesh-graph path first passes through this outer
3280    /// accessor onto the slice and then dispatches per-member through
3281    /// the inner accessors — the two-level dispatch means every per-
3282    /// `:membros` reader now routes through a typed dispatch on the
3283    /// substrate primitive at both altitudes.
3284    ///
3285    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3286    /// inline at three production sites across two files — the
3287    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3288    /// enumerator's `!self.membros.is_empty()` presence probe
3289    /// (caixa-core/src/manifest.rs, which drives the
3290    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3291    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3292    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3293    /// composer's `self.membros.clone()` per-member fold-in path
3294    /// (caixa-core/src/manifest.rs, which materializes the typed
3295    /// [`crate::aplicacao::AplicacaoSpec`] view every
3296    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3297    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3298    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3299    /// [`crate::aplicacao::validate_no_self_membership`] input
3300    /// (caixa-core/src/layout.rs, which pins the "no member names the
3301    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3302    /// extension of the outer `:membros` axis (a per-cluster
3303    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3304    /// admission time so a cluster-specific member-set can tighten a
3305    /// caixa-declared list without re-authoring the `caixa.lisp`,
3306    /// promotion of the plain `Vec<Membro>` to a richer
3307    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3308    /// members land as a typed axis, per-member priority annotation once
3309    /// multi-strategy fan-out lands) would have had to be threaded
3310    /// through all three open-coded copies in lockstep or one consumer
3311    /// would silently disagree with the peers on which member slice a
3312    /// given Caixa resolves to — the enumerator's presence probe reading
3313    /// the raw slot while the peer view-composer's fold-in path read an
3314    /// operator-resolved slot would silently split the paired
3315    /// declared-slot enumerator and typed-view composition, and the
3316    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3317    /// refusal probe reading a third borrow would silently drift the
3318    /// cross-slot coherence gate's traversal input from the two peers, a
3319    /// three-consumer split at the enumerator, the view composer, and
3320    /// the self-membership gate far from the source `caixa.lisp` with no
3321    /// field naming the member-set-drift root cause. Lifting the
3322    /// resolution rule to a typed method on the substrate primitive
3323    /// means every downstream consumer of the caixa's per-`Caixa`
3324    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3325    /// typed dispatch — the resolver's accept-set migrates as a unit on
3326    /// any future axis addition.
3327    ///
3328    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3329    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3330    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3331    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3332    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3333    /// altitude. Peer at the outer altitude of the closed inner-
3334    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3335    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3336    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3337    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3338    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3339    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3340    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3341    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3342    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3343    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3344    /// pattern onto the sibling M3 typed-composite-element axis
3345    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3346    /// [`crate::AplicacaoSpec::membros`] element type at a different
3347    /// altitude).
3348    ///
3349    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3350    /// because every downstream consumer of the member list treats it
3351    /// as a read-only sequence — the slice-view is the narrowest borrow
3352    /// that supports every present + roadmapped consumer (`.iter()`,
3353    /// `.len()`, `.is_empty()`, the
3354    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3355    /// input, `serde` slice-serialization) without leaking the backing
3356    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3357    /// reaches for (the storage-side `Vec` remains reachable through the
3358    /// `pub membros` field for the mutation-carrying serde round-trip
3359    /// and per-test fixture-mutation paths, including the
3360    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3361    /// the typed view). Named `membros()` to match the storage field's
3362    /// name verbatim and the tatara-lisp author-surface term
3363    /// (`:membros`) the field's own docstring already carries; the
3364    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3365    /// vocabulary the [`Caixa::membros`] field's docstring already
3366    /// reaches for ("Member Servicos that make up this Aplicacao").
3367    #[must_use]
3368    pub fn membros(&self) -> &[crate::aplicacao::Membro] {
3369        self.membros.as_slice()
3370    }
3371
3372    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3373    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3374    /// inter-Servico contract-list slice accessor every consumer of the
3375    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3376    /// slice-view keys off — returns the author-declared `:contratos`
3377    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3378    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3379    /// backing buffer the raw `self.contratos.as_slice()` field access
3380    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3381    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3382    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3383    /// single member with no inter-Servico edge carries; the returned
3384    /// `&[WitContract]` degenerates to an empty slice on those arms
3385    /// without any silent `None` collapse).
3386    ///
3387    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3388    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3389    /// container of every per-edge `{de, para, wit, endpoint | subject |
3390    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3391    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3392    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3393    /// adjacency-list seed dispatch on at mesh-artifact materialization
3394    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3395    /// `:membros` vertex set resolves against, closed by the
3396    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3397    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3398    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3399    /// per-edge axis threads through a lifted per-entry accessor on the
3400    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3401    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3402    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3403    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3404    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3405    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3406    /// and the WIT-world discriminant. Every downstream consumer of the
3407    /// mesh-graph edge path first passes through this outer accessor
3408    /// onto the slice and then dispatches per-contract through the
3409    /// inner accessors — the two-level dispatch means every
3410    /// per-`:contratos` reader now routes through a typed dispatch on
3411    /// the substrate primitive at both altitudes.
3412    ///
3413    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3414    /// accessed inline at two production sites in
3415    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3416    /// mesh-slot declared-slot enumerator's
3417    /// `!self.contratos.is_empty()` presence probe (which drives the
3418    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3419    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3420    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3421    /// typed-view composer's `self.contratos.clone()` per-contract
3422    /// fold-in path (which materializes the typed
3423    /// [`crate::aplicacao::AplicacaoSpec`] view every
3424    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3425    /// downstream `caixa-mesh` renderer dispatches on). A future
3426    /// extension of the outer `:contratos` axis (a per-cluster
3427    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3428    /// at admission time so a cluster-specific edge-set can tighten a
3429    /// caixa-declared list without re-authoring the `caixa.lisp`,
3430    /// promotion of the plain `Vec<WitContract>` to a richer
3431    /// `{static, dynamic}` partition once runtime-resolved contract
3432    /// edges land, per-edge policy annotation once the M4 per-edge
3433    /// policy overlay axis lands) would have had to be threaded through
3434    /// both open-coded copies in lockstep or one consumer would
3435    /// silently disagree with the peer on which edge slice a given
3436    /// Caixa resolves to — the enumerator's presence probe reading the
3437    /// raw slot while the peer view-composer's fold-in path read an
3438    /// operator-resolved slot would silently split the paired
3439    /// declared-slot enumerator and typed-view composition, a
3440    /// two-consumer split at the enumerator and the view composer far
3441    /// from the source `caixa.lisp` with no field naming the edge-set-
3442    /// drift root cause. Lifting the resolution rule to a typed method
3443    /// on the substrate primitive means every downstream consumer of
3444    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3445    /// reaches for exactly one typed dispatch — the resolver's
3446    /// accept-set migrates as a unit on any future axis addition.
3447    ///
3448    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3449    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3450    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3451    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3452    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3453    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3454    /// mesh-slot arm of the composite-slice sub-family the sibling
3455    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3456    /// Peer at the outer altitude of the closed inner-
3457    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3458    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3459    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3460    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3461    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3462    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3463    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3464    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3465    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3466    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3467    /// pattern onto the sibling M3 typed-composite-element axis
3468    /// ([`crate::aplicacao::WitContract`] composite, matching the
3469    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3470    /// different altitude).
3471    ///
3472    /// Returns `&[crate::aplicacao::WitContract]` (not
3473    /// `&Vec<WitContract>`) because every downstream consumer of the
3474    /// contract list treats it as a read-only sequence — the slice-view
3475    /// is the narrowest borrow that supports every present + roadmapped
3476    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3477    /// discriminant dispatch, `serde` slice-serialization) without
3478    /// leaking the backing `Vec`'s grow/push/reserve surface no
3479    /// consumer of the typed view reaches for (the storage-side `Vec`
3480    /// remains reachable through the `pub contratos` field for the
3481    /// mutation-carrying serde round-trip and per-test fixture-mutation
3482    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3483    /// clones the slot into the typed view). Named `contratos()` to
3484    /// match the storage field's name verbatim and the tatara-lisp
3485    /// author-surface term (`:contratos`) the field's own docstring
3486    /// already carries; the accessor's identity maps onto the canonical
3487    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3488    /// docstring already reaches for ("WIT-typed inter-Servico
3489    /// contracts").
3490    #[must_use]
3491    pub fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3492        self.contratos.as_slice()
3493    }
3494
3495    /// Compose the Aplicacao-related flat slots into a single typed
3496    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3497    /// downstream renderer consumption. Returns `None` when the
3498    /// caixa isn't a `:kind Aplicacao`.
3499    #[must_use]
3500    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3501        if !self.kind().is_aplicacao() {
3502            return None;
3503        }
3504        Some(crate::aplicacao::AplicacaoSpec {
3505            membros: self.membros().to_vec(),
3506            contratos: self.contratos().to_vec(),
3507            politicas: self.politicas().cloned().unwrap_or_default(),
3508            placement: self.placement().cloned().unwrap_or_default(),
3509            entrada: self.entrada().cloned(),
3510        })
3511    }
3512
3513    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3514    /// *declares* a value on, in canonical declaration order
3515    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3516    /// `:entrada`). A slot counts as declared when its backing field
3517    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3518    ///
3519    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3520    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3521    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3522    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3523    /// caixa-flux / caixa-helm renderers only emit them for an
3524    /// Aplicacao. On any *other* kind a declared mesh slot is the
3525    /// manifest field's documented "ignored otherwise" (see the
3526    /// `:membros` … `:entrada` field docs): it silently passes
3527    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3528    /// rendered — far from the source caixa.lisp.
3529    /// [`crate::StandardLayout::verify`] consults this to reject that
3530    /// silent-drop at caixa-build time
3531    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3532    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3533    /// a slot foreign to the kind is a build error, not a silent drop.
3534    ///
3535    /// Lifted as a typed method (rather than an inline disjunction at
3536    /// the verify call site) so the mesh-slot set lives in one place —
3537    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3538    /// overlay, distributed-app takeover config) is one push here, and
3539    /// every consumer reaching for "which mesh slots are set" (the
3540    /// verify gate, a future `feira lint` kind-coherence advisory)
3541    /// inherits the canonical order without rolling its own.
3542    ///
3543    /// Each per-arm kebab-case label is routed through the peer
3544    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3545    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3546    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3547    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3548    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3549    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3550    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3551    /// kebab-case label + renderer-side artifact key) route through one
3552    /// canonical declaration per arm — same discipline the peer
3553    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3554    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3555    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3556    /// axis, extended here to close the M3 mesh-slot author-facing-label
3557    /// axis so both altitudes of the typed-slot algebra
3558    /// (per-Servico M2 + per-Aplicacao M3) share the same
3559    /// "one canonical byte-string per arm, next to the axis" discipline.
3560    #[must_use]
3561    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3562        let mut slots = Vec::new();
3563        if !self.membros().is_empty() {
3564            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3565        }
3566        if !self.contratos().is_empty() {
3567            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3568        }
3569        if self.politicas().is_some() {
3570            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3571        }
3572        if self.placement().is_some() {
3573            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3574        }
3575        if self.entrada().is_some() {
3576            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3577        }
3578        slots
3579    }
3580
3581    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3582    /// caixa *declares* a value on, in canonical declaration order
3583    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3584    /// `:children`). A slot counts as declared when its backing field
3585    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3586    ///
3587    /// The supervisor-tree slots compose the typed OTP supervisor of a
3588    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3589    /// `:children` field docs above). [`Self::supervisor_view`] only
3590    /// folds them into a validatable [`SupervisorSpec`] when the kind
3591    /// matches (returns `None` otherwise), and the wasm-operator's
3592    /// hierarchical reconciler only consumes them for a Supervisor. On
3593    /// any *other* kind a declared supervisor slot is the manifest
3594    /// field's documented "ignored otherwise" (see the `:estrategia` …
3595    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3596    /// and then vanishes — never validated, never reconciled — far from
3597    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3598    /// this to reject that silent-drop at caixa-build time
3599    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3600    /// exact mirror of the [`Self::declared_mesh_slots`] /
3601    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3602    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3603    /// error, not a silent drop.
3604    #[must_use]
3605    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3606        let mut slots = Vec::new();
3607        if self.estrategia().is_some() {
3608            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3609        }
3610        if self.max_restarts().is_some() {
3611            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3612        }
3613        if self.restart_window().is_some() {
3614            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3615        }
3616        if !self.children().is_empty() {
3617            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3618        }
3619        slots
3620    }
3621
3622    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3623    /// caixa *declares* a value on, in canonical declaration order
3624    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3625    /// declared when its backing field carries a value — a `Some(...)`,
3626    /// or a non-empty `Vec`.
3627    ///
3628    /// The M2 slots configure the runtime of a long-running wasm
3629    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3630    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3631    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3632    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3633    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3634    /// emit these slots for a Servico; on any *other* kind a declared M2
3635    /// slot is the manifest field's documented "ignored otherwise": its
3636    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3637    /// but the value is never rendered into a chart / programs.yaml entry
3638    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
3639    /// vanishes, far from the source caixa.lisp.
3640    /// [`crate::StandardLayout::verify`] consults this to reject that
3641    /// silent-drop at caixa-build time
3642    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
3643    /// mirror of the [`Self::declared_mesh_slots`] /
3644    /// [`Self::declared_supervisor_slots`] gates on the peer
3645    /// kind-exclusive slot sets: a slot foreign to the kind is a build
3646    /// error, not a silent drop.
3647    ///
3648    /// Each per-arm kebab-case label is routed through the peer
3649    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
3650    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
3651    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
3652    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
3653    /// both halves of the M2 top-level slot's dual axis (author-facing
3654    /// kebab-case label + renderer-side camelCase overlay-container wire
3655    /// key) route through one canonical declaration per arm — same
3656    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
3657    /// author-label consts (889dc18) establish on the sibling
3658    /// per-callback axis inside the `:behavior` overlay block.
3659    #[must_use]
3660    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
3661        let mut slots = Vec::new();
3662        if self.limits().is_some() {
3663            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
3664        }
3665        if self.behavior().is_some() {
3666            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
3667        }
3668        if !self.upgrade_from().is_empty() {
3669            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
3670        }
3671        slots
3672    }
3673
3674    /// The kebab-case `:slot` tags of every code-surface slot this caixa
3675    /// declares a value on that its [`CaixaKind`] doesn't natively own,
3676    /// in canonical declaration order (`:exe` → `:servicos`). A
3677    /// code-surface slot is owned by exactly one kind: `:exe` by
3678    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
3679    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
3680    /// `ComputeUnit` daemon surface).
3681    ///
3682    /// Each is silently ignored when declared on the wrong kind: the
3683    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
3684    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
3685    /// code-running kind a declared `:exe` / `:servicos` is the manifest
3686    /// field's documented "ignored otherwise" — its path is checked for
3687    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
3688    /// (which run after [`Caixa::from_lisp`]), but the value is never
3689    /// rendered into a build target or programs.yaml entry. It silently
3690    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
3691    /// caixa.lisp, with no field naming which slot is foreign.
3692    ///
3693    /// [`crate::StandardLayout::verify`] consults this to reject that
3694    /// silent-drop at caixa-build time
3695    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
3696    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
3697    /// gates ([`Self::declared_servico_slots`] /
3698    /// [`Self::declared_supervisor_slots`] /
3699    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
3700    /// axis to be closed on the typed surface. The Supervisor /
3701    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
3702    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
3703    /// diagnostics — they fire ahead of this gate on the same `verify`
3704    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
3705    /// and this method is moot. For Biblioteca / Binario / Servico, this
3706    /// gate fires when a code-running kind declares another code-running
3707    /// kind's exclusive code surface.
3708    ///
3709    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
3710    /// may legitimately ship a `lib/` helper that the underlying
3711    /// substrate (the nix flake for Binario, the wasm component build
3712    /// for Servico) bundles into its build, so the slot's
3713    /// declared-on-wrong-kind cardinality isn't a structural error on
3714    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
3715    /// is the native case (the slot's owning kind). Supervisor /
3716    /// Aplicacao declaring `:bibliotecas` is gated upstream by
3717    /// [`crate::LayoutError::SupervisorOwnsCode`] /
3718    /// [`crate::LayoutError::AplicacaoOwnsCode`].
3719    ///
3720    /// Lifted as a typed method (rather than an inline disjunction at
3721    /// the verify call site) so the foreign-code-slot set lives in one
3722    /// place — a future kind that gains its own code-surface slot is
3723    /// one push here, and every consumer reaching for "which code
3724    /// surfaces are foreign to this kind" (the verify gate, a future
3725    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
3726    /// per-caixa build-target classifier) inherits the canonical order
3727    /// without rolling its own.
3728    #[must_use]
3729    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
3730        let mut slots = Vec::new();
3731        if !self.exe().is_empty() && !self.kind().requires_exe() {
3732            slots.push(":exe");
3733        }
3734        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
3735            slots.push(":servicos");
3736        }
3737        slots
3738    }
3739
3740    /// Validate every entry of `:deps` and `:deps-dev` through
3741    /// [`Dep::validate`] — closing the parity loop with the per-axis
3742    /// `:versao` gates already wired into the typed-graph
3743    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
3744    /// 9888b13) and typed supervisor tree
3745    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
3746    ///
3747    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
3748    /// were the only `:versao` axes still untyped past
3749    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
3750    /// as a String without parsing it, so a malformed-but-non-empty
3751    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
3752    /// silently passed parse and the `semver::Error` surfaced at
3753    /// lacre-resolve time, far from the source caixa.lisp, with no
3754    /// field naming which `:deps` entry carried the typo. Lifting the
3755    /// gate here makes the four `:versao` typed surfaces (`:deps`,
3756    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
3757    /// every requirement string past `validate_deps` is round-trippable
3758    /// through [`crate::parse_requirement`] without re-checking at the
3759    /// resolver layer.
3760    ///
3761    /// Both lists run through the same per-entry validator so a typo
3762    /// in `:deps-dev` surfaces with the same diagnostic as one in
3763    /// `:deps` — neither axis is a second-class citizen of the typed
3764    /// surface.
3765    ///
3766    /// Within each list, [`DepError::DuplicateNome`] closes the
3767    /// set-not-multiset discipline on the `:nome` axis: two entries
3768    /// naming the same caixa carry two `:versao` / `:fonte` / feature
3769    /// triples that the caixa-resolver's lacre pipeline collapses to one
3770    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
3771    /// silently overwrites the first at `concrete_versao`-resolve time
3772    /// (the same "second wins / one silently overwrites the other"
3773    /// shape the peer typed-graph duplicate gates already close on every
3774    /// other Vec-shaped authoring surface that keys by name). The
3775    /// duplicate check fires per-list and runs *after* each per-entry
3776    /// [`Dep::validate`] call so a malformed-and-duplicated entry
3777    /// surfaces its narrower per-entry diagnostic
3778    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
3779    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
3780    /// diagnostic — the canonical "per-entry shape before cross-entry
3781    /// uniqueness" precedence the peer `:children :caixa`
3782    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
3783    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
3784    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
3785    /// ([`crate::AplicacaoSpec::validate_placement`]),
3786    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
3787    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
3788    /// and the within-`:upgrade-from`-entry per-instruction-class
3789    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
3790    /// [`crate::UpgradeError::DuplicateStateChange`],
3791    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
3792    ///
3793    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
3794    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
3795    /// same name in both tables (the dev table's pin overrides the
3796    /// runtime table's pin in test/dev contexts), and caixa's surface
3797    /// mirrors that convention until a deliberate choice retires the
3798    /// override pattern. Only within-list duplicates are structurally
3799    /// incoherent — those are what this gate closes.
3800    pub fn validate_deps(&self) -> Result<(), DepError> {
3801        for &list in crate::dep::DepList::ALL {
3802            let mut seen = std::collections::HashSet::new();
3803            for dep in self.deps_of(list) {
3804                dep.validate()?;
3805                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
3806                    DepError::DuplicateNome {
3807                        nome: dep.nome().to_string(),
3808                        list: list.as_str(),
3809                    }
3810                })?;
3811            }
3812        }
3813        Ok(())
3814    }
3815
3816    /// Reject `:nome` values the K8s apiserver would refuse at admission
3817    /// time. The top-level Caixa identity flows directly into every
3818    /// substrate-side artifact's `metadata.name` axis: the
3819    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
3820    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
3821    /// aggregator keys ComputeUnit derivation off
3822    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
3823    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
3824    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
3825    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
3826    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
3827    /// ([`caixa-mesh::lib::cilium_network_policies`],
3828    /// [`caixa-mesh::lib::gateway_routes`]), and the default
3829    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
3830    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
3831    /// schema enforces the DNS-1123 label rule on admission; a
3832    /// structurally invalid `:nome` (`"MyApp"` — the canonical
3833    /// "I copied the display name verbatim" footgun, `"my_app"` — the
3834    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
3835    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
3836    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
3837    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
3838    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
3839    /// failure surfaced at `kubectl apply` time as a `metadata.name:
3840    /// Invalid value` rejection on whichever derived artifact admitted
3841    /// first, far from the source `caixa.lisp` and without any field
3842    /// naming the offending `:nome`.
3843    ///
3844    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
3845    /// substrate-side predicate the per-axis name gates already share:
3846    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
3847    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
3848    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
3849    /// diagnostic is self-locating (the offending `:nome` is named
3850    /// verbatim) and the author can grep their `caixa.lisp` for
3851    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
3852    /// every per-axis sibling gate already exposes
3853    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
3854    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
3855    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
3856    ///
3857    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
3858    /// derive macro stores the raw String) is gated by the narrower
3859    /// [`ManifestError::NomeEmpty`] arm before the predicate is
3860    /// consulted, mirroring the empty-first cascade every per-axis
3861    /// name gate already uses (e.g. `MembroCaixaEmpty` before
3862    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
3863    pub fn validate_nome(&self) -> Result<(), ManifestError> {
3864        // Routes through the shared
3865        // [`crate::render::require_valid_dns_1123_label`] gate the peer
3866        // name axes each land on so drift between the eight axes'
3867        // accepted DNS-1123-label sets is structurally impossible.
3868        let nome = self.nome();
3869        crate::render::require_valid_dns_1123_label(
3870            nome,
3871            || ManifestError::NomeEmpty,
3872            |reason| ManifestError::NomeInvalid {
3873                nome: nome.to_string(),
3874                reason,
3875            },
3876        )
3877    }
3878
3879    /// Reject `:nome` values whose joint length with the canonical
3880    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
3881    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
3882    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
3883    /// substrate carries materializes the caixa's `:nome` through the
3884    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
3885    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
3886    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
3887    /// `ChartDir.name` + `Chart.yaml::name`
3888    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
3889    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
3890    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
3891    /// `oci://<registry>/lareira-<nome>` chart ref
3892    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
3893    /// admission rule strict-parses against DNS-1123-label, the Helm
3894    /// operator's tracking-secret name is derived from `release_name`
3895    /// and is itself DNS-1123-label-bounded, and the rendered chart's
3896    /// K8s object `metadata.name` axes embed the chart name as a
3897    /// prefix — every one fails admission on a > 63-byte chart name.
3898    ///
3899    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
3900    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
3901    /// `:nome` of 56–63 bytes silently passed validate (the inner
3902    /// DNS-1123 check accepts the bare `:nome`) but produced a
3903    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
3904    /// rejected at admission — far from the source `caixa.lisp`, with
3905    /// no field naming the overflow root cause. The
3906    /// [`lareira_chart_name`] helper's own doc comment
3907    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
3908    /// "the M4 admission webhook will pin the joint-length invariant
3909    /// when it lands". This gate lands the invariant at the
3910    /// manifest-validate layer rather than waiting for the apiserver
3911    /// — the same fail-at-the-source posture every peer per-axis
3912    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
3913    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
3914    /// `:edicao`, etc.) takes.
3915    ///
3916    /// Thin wrapper around
3917    /// [`crate::render::is_lareira_chart_name_shape`] (the
3918    /// substrate-side predicate that composes [`lareira_chart_name`] +
3919    /// [`is_dns_1123_label`] via the lifted
3920    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
3921    /// shared parser-shaped reason into the
3922    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
3923    /// diagnostic is self-locating (the offending `:nome` is named
3924    /// verbatim alongside the rendered chart name and the budget) and
3925    /// the author can shorten in one edit. The gate runs across every
3926    /// `:kind` — `:nome` is the substrate-wide identity axis any
3927    /// future renderer the substrate adds can derive a
3928    /// `lareira-<nome>` artifact from, and uniform enforcement closes
3929    /// the drift footgun where a future kind grows a chart-emitting
3930    /// render path while the validate cascade doesn't catch it.
3931    ///
3932    /// Runs *after* [`Self::validate_nome`] so the narrower
3933    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
3934    /// structurally-malformed `:nome` (empty, uppercase, underscore,
3935    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
3936    /// specific shape error rather than the chart-name-budget error,
3937    /// preserving the legitimate "well-shaped `:nome` that happens to
3938    /// overflow the joint cap" arm for this gate.
3939    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
3940        let nome = self.nome();
3941        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
3942            ManifestError::NomeChartNameBudgetExceeded {
3943                nome: nome.to_string(),
3944                reason,
3945            }
3946        })
3947    }
3948
3949    /// Reject `:versao` values that don't parse as [`semver::Version`].
3950    /// The top-level Caixa version flows directly into every
3951    /// substrate-side artifact that carries a "this is which version of
3952    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
3953    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
3954    /// SemVer-2-strict at `helm template` / `helm install` time per
3955    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
3956    /// `feira publish` Zig-style `v<versao>` git tag
3957    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
3958    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
3959    /// `versao:` value the `lareira-fleet-programs` aggregator carries
3960    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
3961    /// `:latest` tags the substrate's `wasi-service-flake` builds with
3962    /// `skopeo push`, the lacre closure's pinned versions
3963    /// ([`caixa-resolver`] keys `concrete_versao`), and the
3964    /// `:upgrade-from :from` references peers in this exact `versao`
3965    /// shape (`semver::Version`, not `VersionReq`). Each consumer
3966    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
3967    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
3968    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
3969    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
3970    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
3971    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
3972    /// into the version field a peer `:deps :versao` accepts;
3973    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
3974    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
3975    /// derive macro stores the raw String) and the failure surfaced at
3976    /// the *first* downstream consumer that strict-parses it: at
3977    /// `helm install` time as a chart-version rejection, at
3978    /// `feira publish` time as a malformed git tag, at lacre-resolve
3979    /// time as a `semver::Error` not naming the offending caixa, at
3980    /// `feira upgrade --to <versao>` time as an unresolvable
3981    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
3982    /// and without any field naming the offending `:versao`.
3983    ///
3984    /// Thin wrapper around [`semver::Version::parse`] — the same parser
3985    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
3986    /// and [`crate::UpgradeFromEntry::validate`] (the peer
3987    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
3988    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
3989    /// variant, carrying the offending `:versao` verbatim + a
3990    /// parser-shaped reason naming the specific violation, so the
3991    /// diagnostic is self-locating (the author can grep their
3992    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
3993    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
3994    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
3995    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
3996    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
3997    /// now structurally equivalent (every value past validate is
3998    /// round-trippable through [`semver::Version::parse`] without
3999    /// re-checking at the renderer, resolver, or operator hot-upgrade
4000    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4001    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4002    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4003    ///
4004    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4005    /// the derive macro stores the raw String) is gated by the
4006    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4007    /// consulted, mirroring the empty-first cascade every per-axis
4008    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4009    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4010    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4011    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4012        let versao = self.versao();
4013        if versao.is_empty() {
4014            return Err(ManifestError::VersaoEmpty);
4015        }
4016        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4017            versao: versao.to_string(),
4018            reason: e.to_string(),
4019        })?;
4020        Ok(())
4021    }
4022
4023    /// Reject `:restart-window` values the shared
4024    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4025    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4026    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4027    /// `Option<Duration>` routed through the shared codec via `with =
4028    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4029    /// view-construction path ([`Self::supervisor_view`]) folds the
4030    /// raw string through the same shared codec and soft-swallows the
4031    /// parse error as `None` to keep the view best-effort. Without
4032    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4033    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4034    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4035    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4036    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4037    /// edge case) silently produced a `SupervisorSpec` with
4038    /// `restart_window: None`, indistinguishable from the canonical
4039    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4040    /// `MaxIntensity / Period` invariant turns into a never-reset
4041    /// supervisor far from the source `caixa.lisp`, with no field
4042    /// naming the offending `:restart-window`. Lifting the gate to a
4043    /// Caixa-level validator mirrors the trajectory of the peer
4044    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4045    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4046    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4047    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4048    ///
4049    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4050    /// (the shared codec backing `:supervisor :restart-window` as
4051    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4052    /// `:politicas :circuit-breaker :window` — all three covered by
4053    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4054    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4055    /// variant, carrying the offending raw string + a parser-shaped
4056    /// reason naming the canonical authoring form, so the diagnostic
4057    /// is self-locating (the author can grep their `caixa.lisp` for
4058    /// `:restart-window "<value>"` and fix it in one edit) and
4059    /// uniform with every other manifest-level validate diagnostic.
4060    /// With this gate the four `:restart-window`-shaped surfaces (the
4061    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4062    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4063    /// now structurally equivalent — every value past the codec is in
4064    /// one accepted set, by construction.
4065    ///
4066    /// `None` (the canonical "omit the slot to express no reset"
4067    /// shape) is accepted trivially — the gate is a no-op when the
4068    /// author didn't author a window. The empty string is rejected by
4069    /// the shared codec (its digit-only gate refuses an empty
4070    /// magnitude), surfacing the same `RestartWindowMalformed`
4071    /// diagnostic as every other rejected non-canonical shape.
4072    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4073        let Some(s) = self.restart_window() else {
4074            return Ok(());
4075        };
4076        crate::supervisor::duration_codec::parse(s)
4077            .map(|_| ())
4078            .map_err(|reason| ManifestError::RestartWindowMalformed {
4079                restart_window: s.to_string(),
4080                reason,
4081            })
4082    }
4083
4084    /// Reject per-entry values on the three Caixa-level code-surface
4085    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4086    /// layout checker's `root.join(p)` sandbox would silently subvert.
4087    /// Same three structural footguns the peer
4088    /// [`BehaviorSpec::validate`] (b0c8389) and
4089    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4090    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4091    /// `:upgrade-from :state-change :script` axes, here lifted onto
4092    /// the three top-level code-path axes through the shared
4093    /// [`is_sandboxed_relative_path`] predicate:
4094    ///
4095    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4096    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4097    ///     [`Path::join`] as the base itself — `root.join("")` ==
4098    ///     `root`, so the existence check (`self.exists(&root)`)
4099    ///     trivially passes (the project root exists), and the layout
4100    ///     silently treats the project root as a biblioteca / exe /
4101    ///     servico entry. The `:bibliotecas` loop then hands the root
4102    ///     to `tatara_lisp::read` at `feira build` time as if the root
4103    ///     directory itself were a Lisp source file — a parse error
4104    ///     far from the source `caixa.lisp` with no field naming the
4105    ///     offending entry.
4106    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4107    ///     [`Path::join`] *replaces* the base when the right-hand side
4108    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4109    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4110    ///     The existence check then silently consults whatever the
4111    ///     escaped path resolves to — for `:bibliotecas`, the layout
4112    ///     has no `starts_with`-fence (only `:exe` is fenced under
4113    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4114    ///     `:bibliotecas` entry that happens to resolve on disk
4115    ///     silently passes. For `:exe` / `:servicos` the fence catches
4116    ///     the absolute case downstream as `ExeOutsideDir` /
4117    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4118    ///     doesn't exist), but with a downstream-shaped diagnostic
4119    ///     that names the resolved escape path rather than the
4120    ///     authoring footgun at the source.
4121    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4122    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4123    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4124    ///     through [`Path::join`] as a traversal above the caixa root.
4125    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4126    ///     *component-aware* (not canonical-path-aware), so
4127    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4128    ///     is **true** even though the canonical resolution
4129    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4130    ///     — the fence silently lets the parent-escape through, and
4131    ///     the existence check passes if that escape-target happens
4132    ///     to exist. Caught regardless of where the `..` sits
4133    ///     (leading, mid-path, trailing) so the gate matches the peer
4134    ///     predicate's full coverage.
4135    ///
4136    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4137    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4138    /// same per-slot diagnostic shape every peer per-axis path-gate
4139    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4140    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4141    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4142    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4143    /// canonical foreign-code-slot diagnostic, so a manifest with
4144    /// multiple malformed slots surfaces the lexicographically-earliest
4145    /// slot's diagnostic deterministically.
4146    ///
4147    /// Lifted to the typed surface as a Caixa-level validator (peer
4148    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4149    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4150    /// and wired into [`crate::StandardLayout::verify`] before the
4151    /// existence-check loops so the diagnostic names the offending
4152    /// slot at the source caixa.lisp rather than reporting a
4153    /// downstream `MissingEntry` / `ExeOutsideDir` /
4154    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4155    /// The fourth typed code-path surface — every author-supplied
4156    /// path on the manifest — is now structurally accept-shaped
4157    /// past validate, peer with `:behavior :on-*` and
4158    /// `:upgrade-from :state-change :script`.
4159    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4160        /// Per-slot file-type contract for the three Caixa-level
4161        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4162        /// Each variant names the predicate the per-entry file-type
4163        /// gate consults; [`Self::None`] opts the slot out of any
4164        /// file-type contract. Lifted as a typed local enum so the
4165        /// per-slot dispatch is exhaustive at the `match` — adding a
4166        /// future axis to the typed-substrate `:` slot set (the
4167        /// future `:assets` resource axis the M5 roadmap names, the
4168        /// future `:nix-flake` derivation axis the caixa-flake
4169        /// emitter consults) lands as one variant + one `match` arm,
4170        /// not a coordinated rewrite of every per-slot bool flag.
4171        ///
4172        /// Peer of the typed-substrate per-slot variant disciplines
4173        /// already established on this surface
4174        /// ([`crate::supervisor::RestartStrategy`] +
4175        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4176        /// supervision-tree axis,
4177        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4178        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4179        /// `:contratos` payload-target axis): the typed `enum` is
4180        /// the substrate's single source of truth for the per-axis
4181        /// dispatch, and every consumer (the per-arm body here, the
4182        /// future feira-lint per-slot diagnostic renderer, the M4
4183        /// per-axis admission webhook) reaches for the same typed
4184        /// surface rather than re-deriving the partition from inline
4185        /// flag combinations.
4186        enum CodePathFileType {
4187            /// `:exe` — nix-build derivation output, no terminating-
4188            /// extension contract (the canonical `"exe/<name>"`
4189            /// fixtures the layout's `ExeOutsideDir` error message
4190            /// documents carry no extension by convention).
4191            None,
4192            /// `:bibliotecas` — tatara-lisp source files the
4193            /// `feira build` loop reads through `tatara_lisp::read`
4194            /// at parse time. Routes to [`is_lisp_extension`].
4195            LispSource,
4196            /// `:servicos` — ComputeUnit-CR YAML files the
4197            /// caixa-helm / caixa-flux renderers consume through
4198            /// `serde_yaml::from_str`. Routes to
4199            /// [`is_computeunit_yaml_extension`].
4200            ComputeUnitYaml,
4201        }
4202
4203        // The per-slot [`CodePathFileType`] selects which axes carry the
4204        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4205        // source axis (the `feira build` loop at
4206        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4207        // `tatara_lisp::read` at parse time) — the lifted
4208        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4209        // `:exe` is the nix-built executable surface (per the canonical
4210        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4211        // error message documents and every in-tree
4212        // `caixa_with_code_paths` positive control uses) — its file-type
4213        // contract is "nix-build derivation output", not a typed source
4214        // file, so [`CodePathFileType::None`] opts the slot out of any
4215        // file-type gate. `:servicos` is the `.computeunit.yaml`
4216        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4217        // renderers consume each entry through `serde_yaml::from_str` as
4218        // a typed `ComputeUnit` CR) — the lifted
4219        // [`is_computeunit_yaml_extension`] predicate gates the compound
4220        // `.computeunit.yaml` suffix. All three axes are surfaced through
4221        // the same iteration so the sandbox-shape + duplicate gates
4222        // apply uniformly; the typed file-type dispatch fires per-slot
4223        // exactly where the downstream consumer's accepted set demands
4224        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4225        // compounding lift on the peer 64772a9 `:bibliotecas`
4226        // `.lisp`-gate trajectory — the second of the three code-path
4227        // axes to land on a typed compound-suffix gate, with the same
4228        // self-locating per-slot diagnostic shape every peer per-axis
4229        // file-type lift uses (`*NonLispExtension { slot, path }` /
4230        // `*NonComputeUnitYamlExtension { slot, path }`).
4231        for (slot, list, file_type) in [
4232            (
4233                ":bibliotecas",
4234                &self.bibliotecas,
4235                CodePathFileType::LispSource,
4236            ),
4237            (":exe", &self.exe, CodePathFileType::None),
4238            (
4239                ":servicos",
4240                &self.servicos,
4241                CodePathFileType::ComputeUnitYaml,
4242            ),
4243        ] {
4244            // Per-slot set-not-multiset gate on the typed code-path axis.
4245            // Every peer Vec-shaped author-supplied list past validate is
4246            // a set, not a multiset: `:membros :caixa`
4247            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4248            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4249            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4250            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4251            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4252            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4253            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4254            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4255            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4256            // the three code-path lists are the last Vec-shaped author-
4257            // supplied slots on the typed Caixa surface still admitting a
4258            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4259            // duplicates are flagged within `:bibliotecas`, not across
4260            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4261            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4262            // legitimate dev-vs-runtime shape on the dep axis, fenced
4263            // separately by [`crate::dep::validate_no_self_dep`]). On the
4264            // code-path axis a cross-slot collision is structurally
4265            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4266            // fence — `:exe` and `:servicos` entries are confined to their
4267            // own directory trees, so the only way a string could appear
4268            // on two code-path lists is the (rare, structurally invalid)
4269            // case where `:bibliotecas` carries an `"exe/<x>"` or
4270            // `"servicos/<x>.yaml"`-shaped path.
4271            //
4272            // Without the gate three authoring footguns silently passed:
4273            //
4274            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4275            //     canonical copy-paste-the-wrong-file footgun. `feira
4276            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4277            //     list and re-parses the same file twice, wasting work
4278            //     and silently masking the author's intent to declare a
4279            //     *second* biblioteca.
4280            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4281            //     Binario surface. The future `caixa-flake` `nix flake`
4282            //     emitter that materializes each `:exe` entry as a flake
4283            //     `packages.<exe-name>` derivation would collide on the
4284            //     duplicate package name and surface a flake-eval error
4285            //     far from the source `caixa.lisp`.
4286            //   - `:servicos ("servicos/x.computeunit.yaml"
4287            //     "servicos/x.computeunit.yaml")` — the same footgun on
4288            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4289            //     renderers already refuse `:servicos.len() != 1` with
4290            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4291            //     that diagnostic surfaces "too many servicos" without
4292            //     naming "duplicate entry" — the typed self-locating
4293            //     "which entry is the duplicate" framing only lands at
4294            //     this gate.
4295            //
4296            // Same `seen.insert(entry.as_str())` shape every peer per-list
4297            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4298            // 86c769b, `:deps` 359fba5) and the same "structural shape
4299            // checks fire before the duplicate check on the same entry"
4300            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4301            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4302            // empty entry first, not the duplicate on the later pair).
4303            let mut seen = std::collections::HashSet::new();
4304            for entry in list {
4305                let path = Path::new(entry);
4306                match is_sandboxed_relative_path(path) {
4307                    Ok(()) => {}
4308                    Err(PathShapeViolation::Empty) => {
4309                        return Err(ManifestError::CodePathEmpty { slot });
4310                    }
4311                    Err(PathShapeViolation::Absolute) => {
4312                        return Err(ManifestError::CodePathAbsolute {
4313                            slot,
4314                            path: path.to_path_buf(),
4315                        });
4316                    }
4317                    Err(PathShapeViolation::ParentEscape) => {
4318                        return Err(ManifestError::CodePathParentEscape {
4319                            slot,
4320                            path: path.to_path_buf(),
4321                        });
4322                    }
4323                }
4324                // The per-slot file-type gate dispatched through the
4325                // typed [`CodePathFileType`] selector above. Each variant
4326                // routes to the lifted predicate the downstream consumer
4327                // demands:
4328                //
4329                //   - [`LispSource`] → [`is_lisp_extension`] for
4330                //     `:bibliotecas` (the `feira build` loop's
4331                //     `tatara_lisp::read` consumer);
4332                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4333                //     for `:servicos` (the caixa-helm / caixa-flux
4334                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4335                //     accepted set);
4336                //   - [`None`] for `:exe` — the nix-build derivation-
4337                //     output axis has no terminating-extension contract.
4338                //
4339                // Fires after the sandbox-shape arms so a path that is
4340                // *both* sandbox-escaping and wrong-extension surfaces
4341                // the more fundamental sandbox-shape diagnostic first
4342                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4343                // `ParentEscape` → `NonLispExtension` arm-ordering on
4344                // `:behavior :on-*` c97815a, and `EmptyScript` →
4345                // `AbsoluteScript` → `ParentEscapeScript` →
4346                // `NonLispExtensionScript` on
4347                // `:upgrade-from :state-change :script` 33cc830), and
4348                // before the duplicate gate so the narrower per-entry
4349                // file-type shape dominates the cross-entry uniqueness
4350                // diagnostic (a
4351                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4352                // `:servicos` surfaces
4353                // `CodePathNonComputeUnitYamlExtension` on the first
4354                // entry rather than `CodePathDuplicate` on the pair —
4355                // peer with the 64772a9 `:bibliotecas`
4356                // `("lib/x.txt" "lib/x.txt")` ordering).
4357                match file_type {
4358                    CodePathFileType::None => {}
4359                    CodePathFileType::LispSource => {
4360                        if !is_lisp_extension(path) {
4361                            return Err(ManifestError::CodePathNonLispExtension {
4362                                slot,
4363                                path: path.to_path_buf(),
4364                            });
4365                        }
4366                    }
4367                    CodePathFileType::ComputeUnitYaml => {
4368                        if !is_computeunit_yaml_extension(path) {
4369                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4370                                slot,
4371                                path: path.to_path_buf(),
4372                            });
4373                        }
4374                    }
4375                }
4376                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4377                    ManifestError::CodePathDuplicate {
4378                        slot,
4379                        path: path.to_path_buf(),
4380                    }
4381                })?;
4382            }
4383        }
4384        Ok(())
4385    }
4386
4387    /// Reject `:etiquetas` lists with an empty entry or with two entries
4388    /// agreeing on the same string. `:etiquetas` is the universal
4389    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4390    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4391    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4392    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4393    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4394    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4395    /// Two authoring footguns silently passed validate without this gate:
4396    ///
4397    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4398    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4399    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4400    ///     `chart.metadata.keywords` admits the value without a strict
4401    ///     parser-side gate, but the empty keyword has no operational
4402    ///     meaning — it indexes nothing in the future caixa-registry
4403    ///     search axis and clutters the rendered chart with a no-op tag.
4404    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4405    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4406    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4407    ///     at chart render — a "second wins / one silently disappears"
4408    ///     shape divergent from every peer typed-graph set gate
4409    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4410    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4411    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4412    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4413    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4414    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4415    ///     on `:upgrade-from`, the per-instruction-class singularity
4416    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4417    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4418    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4419    ///     discipline is uniform: every Vec-shaped author-supplied list
4420    ///     past validate is set-not-multiset, by construction.
4421    ///
4422    /// Past the empty arm the gate enforces the chart-keyword shape
4423    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4424    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4425    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4426    /// continuation. Closes the canonical paste-from-doc footguns the
4427    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4428    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4429    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4430    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4431    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4432    /// — the author meant three separate list entries), path-separator
4433    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4434    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4435    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4436    /// control bytes that would silently land as malformed search tags
4437    /// in the rendered Chart.yaml `keywords:` array and break the
4438    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4439    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4440    /// established on the sibling universal-axis `Vec<String>` surface
4441    /// — the second universal-axis Vec<String> surface to land the
4442    /// empty-first-then-shape-then-duplicate per-entry cascade.
4443    ///
4444    /// Same empty-first cascade discipline every peer per-axis gate
4445    /// uses: the per-entry empty arm fires before the per-entry shape
4446    /// arm fires before the cross-entry duplicate arm, so an
4447    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4448    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4449    /// has no value" defect) before either the shape or the duplicate
4450    /// diagnostic. Walks the list in declaration order so the
4451    /// first-collision diagnostic surfaces the lexicographically-
4452    /// earliest offending position, peer with every other duplicate
4453    /// gate on this surface.
4454    ///
4455    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4456    /// caixa-build gate alongside the peer universal gates
4457    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4458    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4459    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4460    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4461    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4462    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4463    /// slot sets. The future caixa-registry search axis can reach for
4464    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4465    /// chart-keyword-shaped string without re-deriving the precondition.
4466    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4467        let mut seen = std::collections::HashSet::new();
4468        for etiqueta in self.etiquetas() {
4469            if etiqueta.is_empty() {
4470                return Err(ManifestError::EtiquetaEmpty);
4471            }
4472            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4473                ManifestError::EtiquetaInvalid {
4474                    etiqueta: etiqueta.clone(),
4475                    reason,
4476                }
4477            })?;
4478            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4479                ManifestError::EtiquetaDuplicate {
4480                    etiqueta: etiqueta.clone(),
4481                }
4482            })?;
4483        }
4484        Ok(())
4485    }
4486
4487    /// Reject `:autores` lists with an empty entry or with two entries
4488    /// agreeing on the same string. `:autores` is the universal
4489    /// maintainer-axis on [`Caixa`] (every kind carries the
4490    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4491    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4492    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4493    /// to a `Maintainer { name, email: None }` without dedup). Two
4494    /// authoring footguns silently passed validate without this gate:
4495    ///
4496    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4497    ///     blank-doc footgun) rendered as
4498    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4499    ///     empty maintainer name has no operational meaning — it
4500    ///     identifies no one in the substrate's authorship index and
4501    ///     clutters the rendered chart with a no-op maintainer.
4502    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4503    ///     the copy-paste-the-wrong-author footgun) silently passed
4504    ///     validate and rendered as two identical maintainer entries.
4505    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4506    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4507    ///     rendered `keywords:` array at chart-render time), the
4508    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4509    ///     entries stack verbatim in the chart, divergent from every
4510    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4511    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4512    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4513    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4514    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4515    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4516    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4517    ///     `:etiquetas`).
4518    ///
4519    /// Past the empty arm the gate enforces the chart-maintainer-name
4520    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4521    /// the structural single-line printable-UTF-8 floor every realistic
4522    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4523    /// or trailing whitespace, no ASCII control characters anywhere,
4524    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4525    /// footguns the bare empty + duplicate arms left open:
4526    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4527    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4528    /// pasted a multi-line block of author records into one `:autores`
4529    /// entry instead of splitting into one entry per author),
4530    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4531    /// and the paste-from-binary-blob control bytes that would silently
4532    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4533    /// `maintainers:` array. Mirrors the shape-predicate cascade
4534    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4535    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4536    /// establish past their own empty arms on the sibling universal-axis
4537    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4538    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4539    /// cascade.
4540    ///
4541    /// Same empty-first cascade discipline every peer per-axis gate
4542    /// uses: the per-entry empty arm fires before the per-entry shape
4543    /// arm before the cross-entry duplicate arm. Walks the list in
4544    /// declaration order so the first-collision diagnostic surfaces the
4545    /// lexicographically-earliest offending position, peer with every
4546    /// other duplicate gate on this surface.
4547    ///
4548    /// Universal-axis (every kind carries `:autores`), so wired at the
4549    /// caixa-build gate alongside the peer universal gates
4550    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4551    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4552    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4553    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4554    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4555    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4556    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4557    /// slot sets.
4558    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4559        let mut seen = std::collections::HashSet::new();
4560        for autor in self.autores() {
4561            if autor.is_empty() {
4562                return Err(ManifestError::AutorEmpty);
4563            }
4564            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4565                ManifestError::AutorInvalid {
4566                    autor: autor.clone(),
4567                    reason,
4568                }
4569            })?;
4570            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4571                ManifestError::AutorDuplicate {
4572                    autor: autor.clone(),
4573                }
4574            })?;
4575        }
4576        Ok(())
4577    }
4578
4579    /// Reject `:repositorio` values whose shape the shared
4580    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4581    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4582    /// universal git-shaped homepage axis every kind carries — the
4583    /// substrate routes the same string through two load-bearing
4584    /// consumers:
4585    ///
4586    ///   - [`caixa-helm`] folds it verbatim into the rendered
4587    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4588    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4589    ///     the chart `README.md` `repo = …` interpolation
4590    ///     (`caixa-helm/src/lib.rs:359`).
4591    ///   - [`caixa-flux`] folds it verbatim into the standalone
4592    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4593    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4594    ///     `GitRepository.spec.url` the cluster's source-controller
4595    ///     polls — the load-bearing deploy-time axis.
4596    ///
4597    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4598    /// substitute a placeholder when the slot is absent (`None` → the
4599    /// fallback fires); a `Some("")` *skips the fallback* and silently
4600    /// passes the empty string through to `Chart.yaml home: ""` /
4601    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4602    /// controller both reject the empty URL far from the source
4603    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4604    /// Similarly a malformed `:repositorio` (whitespace, control char,
4605    /// missing `:` separator, leading `-`) silently lands in the
4606    /// rendered artifacts and breaks at `git clone` / `helm template`
4607    /// / `flux reconcile` time.
4608    ///
4609    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4610    /// same shared predicate the peer [`crate::DepSource::validate`]
4611    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4612    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4613    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4614    /// structurally equivalent: every value past validate is
4615    /// guaranteed-acceptable by the predicate's union of constraints
4616    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4617    /// control chars, ASCII only, no leading `:`, contains a `:`
4618    /// separator). The predicate accepts every documented authoring
4619    /// shape — `github:org/repo` shorthand, `https://host/path`,
4620    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4621    /// scp-style SSH, `file:///path` — and refuses the canonical
4622    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4623    /// injection footguns at validate time. Maps the predicate's
4624    /// `String` reason verbatim into the
4625    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4626    /// offending value + parser-shaped reason so the diagnostic is
4627    /// self-locating (the author can grep their `caixa.lisp` for
4628    /// `:repositorio "<value>"` and fix it in one edit).
4629    ///
4630    /// `None` (the canonical "omit the slot to express no published
4631    /// homepage" shape) is accepted trivially — the gate is a no-op
4632    /// when the author didn't declare a value. `Some("")` is gated by
4633    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4634    /// shape predicate is consulted, mirroring the empty-first cascade
4635    /// every peer per-axis identity gate uses
4636    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
4637    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
4638    /// [`crate::DepError::FonteRepoEmpty`] →
4639    /// [`crate::DepError::FonteRepoInvalid`]).
4640    ///
4641    /// Universal-axis (every kind carries `:repositorio`), so wired at
4642    /// the caixa-build gate alongside the peer universal gates
4643    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4644    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4645    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
4646    /// before the kind-coherence gates
4647    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4648    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4649    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4650    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4651    /// specific slot sets.
4652    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
4653        let Some(s) = self.repositorio() else {
4654            return Ok(());
4655        };
4656        if s.is_empty() {
4657            return Err(ManifestError::RepositorioEmpty);
4658        }
4659        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
4660            repositorio: s.to_string(),
4661            reason,
4662        })
4663    }
4664
4665    /// Reject `:descricao` values that are the empty string. The flat
4666    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
4667    /// free-form-prose homepage axis every kind carries — the
4668    /// substrate routes the same string through two load-bearing
4669    /// consumers in the [`caixa-helm`] renderer:
4670    ///
4671    ///   - `build_chart_yaml` folds it verbatim into the rendered
4672    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
4673    ///     field (`caixa-helm/src/lib.rs:232-235`).
4674    ///   - `build_readme` folds it verbatim into the rendered chart
4675    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
4676    ///
4677    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4678    /// substitute a `caixa.nome`-derived placeholder when the slot is
4679    /// absent (`None` → the fallback fires); a `Some("")` *skips the
4680    /// fallback* and silently passes the empty string through to
4681    /// `Chart.yaml description: ""` / a blank chart `README.md`
4682    /// header. Helm's chart spec requires a non-empty `description:`
4683    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
4684    /// `WARNING [chart.metadata.description]: description is required`),
4685    /// so the empty `Some("")` silently lands in the rendered
4686    /// artifacts and breaks at `helm lint` / `helm install` time far
4687    /// from the source `caixa.lisp`, with no field naming the
4688    /// offending `:descricao`.
4689    ///
4690    /// `None` (the canonical "omit the slot to defer to the renderer's
4691    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
4692    /// the gate is a no-op when the author didn't declare a value.
4693    /// `Some("")` is gated by the narrower
4694    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
4695    /// shape every peer per-axis empty gate uses
4696    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4697    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4698    /// [`ManifestError::RepositorioEmpty`]).
4699    ///
4700    /// Universal-axis (every kind carries `:descricao`), so wired at
4701    /// the caixa-build gate alongside the peer universal gates
4702    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4703    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4704    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4705    /// [`Self::validate_code_paths`] — before the kind-coherence
4706    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4707    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4708    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4709    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4710    /// specific slot sets.
4711    ///
4712    /// Past the empty arm the gate enforces the chart-description
4713    /// shape predicate via [`crate::render::is_chart_description_shape`]:
4714    /// the structural single-line UTF-8 floor every realistic chart
4715    /// description in the wild matches — 1..=512 bytes, no leading
4716    /// or trailing whitespace, no ASCII control characters anywhere
4717    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
4718    /// carriage return, and every other control byte), Unicode
4719    /// continuation bytes accepted (the canonical fixtures carry
4720    /// `→` and `—`). Closes the canonical paste-from-doc footguns
4721    /// the bare empty-arm gate left open: paste-from-aligned-doc
4722    /// leading / trailing whitespace (`" Checkout flow."`,
4723    /// `"Checkout flow. "`), paste-from-multiline-doc newline
4724    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
4725    /// (`"Checkout\rflow."`), tab-from-aligned-doc
4726    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
4727    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
4728    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
4729    /// [`Self::validate_edicao`] establish past their own empty arms
4730    /// on the sibling universal-axis `Option<String>` Caixa-level
4731    /// value-shape surfaces.
4732    ///
4733    /// The empty-first cascade discipline mirrors every peer per-axis
4734    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
4735    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
4736    /// diagnostic surfaces on `Some("")` rather than the broader
4737    /// shape-predicate diagnostic — peer with how
4738    /// [`ManifestError::LicencaEmpty`] runs before
4739    /// [`ManifestError::LicencaInvalid`],
4740    /// [`ManifestError::EdicaoEmpty`] runs before
4741    /// [`ManifestError::EdicaoInvalid`],
4742    /// [`ManifestError::RepositorioEmpty`] runs before
4743    /// [`ManifestError::RepositorioInvalid`].
4744    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
4745        let Some(s) = self.descricao() else {
4746            return Ok(());
4747        };
4748        if s.is_empty() {
4749            return Err(ManifestError::DescricaoEmpty);
4750        }
4751        crate::render::is_chart_description_shape(s).map_err(|reason| {
4752            ManifestError::DescricaoInvalid {
4753                descricao: s.to_string(),
4754                reason,
4755            }
4756        })?;
4757        Ok(())
4758    }
4759
4760    /// Reject `:licenca` values that are the empty string. The flat
4761    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
4762    /// SPDX-shaped license-expression axis every kind carries — the
4763    /// substrate routes the same string through the [`caixa-helm`]
4764    /// renderer's `build_readme` which folds it verbatim into the
4765    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
4766    /// section (`caixa-helm/src/lib.rs:361`) via
4767    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
4768    /// fallback only fires on `None`; a `Some("")` *skips the
4769    /// fallback* and silently passes the empty string through to a
4770    /// chart `README.md` whose `License` section renders as the bare
4771    /// trailing period (`.\n`) — peer footgun with the
4772    /// `Some("")`-skips-`unwrap_or_else` shape the
4773    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
4774    /// gates close on the sibling free-form-prose and git-URL axes.
4775    ///
4776    /// `None` (the canonical "omit the slot to defer to the
4777    /// renderer's `MIT` fallback" shape every existing fixture
4778    /// carries) is accepted trivially — the gate is a no-op when the
4779    /// author didn't declare a value. `Some("")` is gated by the
4780    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
4781    /// empty-arm shape every peer per-axis empty gate uses
4782    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4783    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4784    /// [`ManifestError::RepositorioEmpty`],
4785    /// [`ManifestError::DescricaoEmpty`]).
4786    ///
4787    /// Universal-axis (every kind carries `:licenca`), so wired at
4788    /// the caixa-build gate alongside the peer universal gates
4789    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4790    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4791    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4792    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
4793    /// — before the kind-coherence gates
4794    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4795    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4796    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4797    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4798    /// specific slot sets.
4799    ///
4800    /// Past the empty arm the gate enforces the SPDX-expression shape
4801    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
4802    /// structural alphabet floor every realistic SPDX expression in
4803    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
4804    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
4805    /// single ASCII space (token separator). Closes the canonical
4806    /// paste-from-doc footguns the bare empty-arm gate left open:
4807    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
4808    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
4809    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
4810    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
4811    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
4812    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
4813    /// Apache-2.0"`), and semicolon-list-separator confusion
4814    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
4815    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
4816    /// establish past their own empty arms.
4817    ///
4818    /// The empty-first cascade discipline mirrors every peer per-axis
4819    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
4820    /// [`ManifestError::LicencaInvalid`], so the narrower empty
4821    /// diagnostic surfaces on `Some("")` rather than the broader
4822    /// shape-predicate diagnostic — peer with how
4823    /// [`ManifestError::EdicaoEmpty`] runs before
4824    /// [`ManifestError::EdicaoInvalid`],
4825    /// [`ManifestError::RepositorioEmpty`] runs before
4826    /// [`ManifestError::RepositorioInvalid`].
4827    ///
4828    /// A future tightening on this axis can extend the alphabet
4829    /// floor into a full SPDX expression parser + license-id
4830    /// allowlist (rejecting alphabet-valid values that don't name a
4831    /// real SPDX license identifier — e.g., `"NotAReal"` is
4832    /// alphabet-valid but no `NotAReal` license-id exists). That
4833    /// parser only becomes meaningful past a real SPDX-spec
4834    /// dependency; this gate establishes the structural floor by
4835    /// refusing every non-SPDX-alphabet value at validate time.
4836    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
4837        let Some(s) = self.licenca() else {
4838            return Ok(());
4839        };
4840        if s.is_empty() {
4841            return Err(ManifestError::LicencaEmpty);
4842        }
4843        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
4844            ManifestError::LicencaInvalid {
4845                licenca: s.to_string(),
4846                reason,
4847            }
4848        })?;
4849        Ok(())
4850    }
4851
4852    /// Reject `:edicao` values that are the empty string. The flat
4853    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
4854    /// language-edition axis every kind carries — it determines the
4855    /// tatara-lisp macro surface + compatibility flags the substrate
4856    /// applies when building a caixa, and lands verbatim in the
4857    /// `Caixa::template` author-time scaffold (the canonical
4858    /// `:edicao "2026"` line every `feira init` emits via
4859    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
4860    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
4861    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
4862    /// `caixa-core/src/render.rs:2510`) via
4863    /// `edicao: Some("2026".into())`.
4864    ///
4865    /// `None` (the canonical "omit the slot to defer to the
4866    /// substrate's default edition" shape every existing
4867    /// [`caixa-resolver`] integration test fixture carries via
4868    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4869    /// is accepted trivially — the gate is a no-op when the author
4870    /// didn't declare a value. `Some("")` is gated by the narrower
4871    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
4872    /// shape every peer per-axis empty gate uses
4873    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
4874    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
4875    /// [`ManifestError::RepositorioEmpty`],
4876    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
4877    ///
4878    /// Universal-axis (every kind carries `:edicao`), so wired at
4879    /// the caixa-build gate alongside the peer universal gates
4880    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4881    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4882    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
4883    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4884    /// [`Self::validate_code_paths`] — before the kind-coherence
4885    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4886    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4887    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4888    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
4889    /// specific slot sets.
4890    ///
4891    /// Past the empty arm the gate enforces the canonical year-shape
4892    /// predicate: every documented tatara-lisp edition is a 4-digit
4893    /// ASCII decimal year (`"2026"` is the only edition currently
4894    /// minted; future-introduced siblings will follow the same
4895    /// shape, peer with Cargo's `[package] edition` grammar which
4896    /// every value Cargo has ever accepted matches — `"2015"`,
4897    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
4898    /// 4 ASCII decimal bytes is rejected with the narrower
4899    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
4900    /// shape-predicate cascade [`Self::validate_repositorio`]
4901    /// establishes past its own empty arm
4902    /// ([`ManifestError::RepositorioEmpty`] →
4903    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
4904    /// paste-from-doc footguns the bare empty-arm gate left open:
4905    ///
4906    ///   - leading / trailing whitespace from a paste-from-doc
4907    ///     (`"2026 "`, `" 2026"`)
4908    ///   - control characters / CRLF from a paste-from-multiline-doc
4909    ///     (`"2026\n"`)
4910    ///   - non-ASCII look-alikes from a fullwidth keyboard
4911    ///     (`"2026"`) which would silently land as a non-ASCII
4912    ///     string in the rendered caixa.lisp
4913    ///   - free-form non-year values (`"x"`, `"latest"`,
4914    ///     `"nightly"`) that have no operational meaning on the
4915    ///     substrate's build-time edition selector
4916    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
4917    ///     `"r2026"`) — common version-tag idioms that don't apply
4918    ///     to the year-shaped edition axis
4919    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
4920    ///     edition is a year, not a fractional version
4921    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
4922    ///     `"00026"`) that don't name a year
4923    ///
4924    /// `None` (the canonical "omit the slot to defer to the
4925    /// substrate's default edition" shape every existing
4926    /// [`caixa-resolver`] integration test fixture carries via
4927    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
4928    /// is accepted trivially — the gate is a no-op when the author
4929    /// didn't declare a value. The empty-first cascade discipline
4930    /// mirrors every peer per-axis identity gate:
4931    /// [`ManifestError::EdicaoEmpty`] runs before
4932    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
4933    /// diagnostic surfaces on `Some("")` rather than the broader
4934    /// shape-predicate diagnostic — peer with how
4935    /// [`ManifestError::NomeEmpty`] runs before
4936    /// [`ManifestError::NomeInvalid`],
4937    /// [`ManifestError::VersaoEmpty`] runs before
4938    /// [`ManifestError::VersaoInvalid`],
4939    /// [`ManifestError::RepositorioEmpty`] runs before
4940    /// [`ManifestError::RepositorioInvalid`].
4941    ///
4942    /// A future tightening on this axis can extend the shape
4943    /// predicate into a known-edition allowlist (rejecting
4944    /// year-shaped values that don't name a tatara-lisp edition
4945    /// the substrate actually understands — e.g., `"1999"` is
4946    /// year-shaped but no `1999` edition exists). That allowlist
4947    /// only becomes meaningful past the introduction of a sibling
4948    /// edition to `"2026"`; this gate establishes the structural
4949    /// floor by refusing every non-year-shaped value at validate
4950    /// time.
4951    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
4952        let Some(s) = self.edicao() else {
4953            return Ok(());
4954        };
4955        if s.is_empty() {
4956            return Err(ManifestError::EdicaoEmpty);
4957        }
4958        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
4959            return Err(ManifestError::EdicaoInvalid {
4960                edicao: s.to_string(),
4961                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
4962            });
4963        }
4964        Ok(())
4965    }
4966
4967    /// Compose the supervisor-related flat slots into a single
4968    /// [`SupervisorSpec`] for validation. Returns `None` when the
4969    /// caixa isn't a `:kind Supervisor`.
4970    ///
4971    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
4972    /// simple (one form, no nested `:supervisor (…)` block); this view
4973    /// is the "typed shape" the operator + supervisor reconciler
4974    /// consume.
4975    #[must_use]
4976    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
4977        if !self.kind().is_supervisor() {
4978            return None;
4979        }
4980        // Fold through the shared `supervisor::duration_codec::parse`
4981        // — the same parser the serde-routed `with = "duration_codec"`
4982        // on `SupervisorSpec::restart_window`, the `:politicas
4983        // :timeout` codec, and the `:politicas :circuit-breaker
4984        // :window` codec all consume. The prior inline f64-shaped
4985        // duplicate (`parse_window_inline`) admitted every magnitude
4986        // the integer-magnitude gate (1c55a2a) rejects on the three
4987        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
4988        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
4989        // `None` (i.e. "no reset"), divergent from the shared codec's
4990        // integer-magnitude discipline by construction. The fold
4991        // closes the divergence: every value the typed
4992        // `SupervisorSpec` carries past `supervisor_view` is in the
4993        // shared codec's accepted set. The `.ok()` here preserves the
4994        // existing soft-swallow shape on this view-construction path;
4995        // the new [`Caixa::validate_restart_window`] (sibling of
4996        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
4997        // the offending raw string at build time so authoring tools
4998        // (`feira lint`, the future layout-side wire-up) surface a
4999        // self-locating diagnostic instead of a silently dropped
5000        // window.
5001        let restart_window = self
5002            .restart_window()
5003            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5004        Some(SupervisorSpec {
5005            estrategia: self.estrategia().unwrap_or_default(),
5006            max_restarts: self.max_restarts().unwrap_or(5),
5007            restart_window,
5008            children: self.children().to_vec(),
5009        })
5010    }
5011
5012    /// A minimal starter manifest emitted by `feira init`.
5013    #[must_use]
5014    pub fn template(nome: &str) -> String {
5015        format!(
5016            "(defcaixa\n  \
5017               :nome        {nome:?}\n  \
5018               :versao      \"0.1.0\"\n  \
5019               :kind        Biblioteca\n  \
5020               :edicao      \"2026\"\n  \
5021               :descricao   \"FIXME — describe this caixa\"\n  \
5022               :autores     ()\n  \
5023               :etiquetas   ()\n  \
5024               :deps        ()\n  \
5025               :deps-dev    ()\n  \
5026               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5027        )
5028    }
5029
5030    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5031    /// back after mutation (e.g. `feira add`).
5032    ///
5033    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5034    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5035    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5036    #[must_use]
5037    pub fn to_lisp(&self) -> String {
5038        let json = serde_json::to_value(self).expect("Caixa serialize");
5039        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5040        let tatara_lisp::Sexp::List(items) = sexp else {
5041            return format!("(defcaixa {sexp})\n");
5042        };
5043        let mut out = String::from("(defcaixa");
5044        let mut i = 0;
5045        while i + 1 < items.len() {
5046            out.push_str("\n  ");
5047            out.push_str(&items[i].to_string());
5048            out.push(' ');
5049            out.push_str(&items[i + 1].to_string());
5050            i += 2;
5051        }
5052        out.push_str(")\n");
5053        out
5054    }
5055}
5056
5057/// Errors raised by top-level [`Caixa`] validators that don't fit
5058/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5059/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5060/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5061/// through every substrate-side artifact's `metadata.name` /
5062/// version derivation.
5063///
5064/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5065/// doc-comment anticipates) can hold one of each per-axis error
5066/// family without reshaping individual diagnostics; this enum is
5067/// the first such per-Caixa-identity family.
5068#[derive(Debug, Error, PartialEq, Eq)]
5069pub enum ManifestError {
5070    #[error(
5071        ":nome is empty (every caixa must name itself; the value flows \
5072         into every K8s artifact's `metadata.name` derivation and into \
5073         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5074    )]
5075    NomeEmpty,
5076    #[error(
5077        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5078         apiserver enforces this rule on every `metadata.name` the \
5079         caixa's substrate-side renderers derive from `:nome` — the \
5080         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5081         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5082         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5083         name; use a lowercase alphanumeric + hyphen identifier like \
5084         `\"checkout\"` or `\"cart-v2\"`)"
5085    )]
5086    NomeInvalid { nome: String, reason: String },
5087    #[error(
5088        ":nome {nome:?} overflows the joint-length budget on the canonical \
5089         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5090         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5091         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5092         `chart:` slot, `caixa-tatara`'s `release_name` + \
5093         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5094         joint name through the canonical `lareira_chart_name` helper, and \
5095         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5096         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5097         reject any joint name exceeding 63 bytes; the narrower \
5098         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5099         arm gates the chart-name budget downstream renderers inherit)"
5100    )]
5101    NomeChartNameBudgetExceeded { nome: String, reason: String },
5102    #[error(
5103        ":versao is empty (every caixa must pin its own version; the value flows \
5104         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5105         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5106         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5107         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5108    )]
5109    VersaoEmpty,
5110    #[error(
5111        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5112         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5113         with optional `-prerelease` and `+build` — across every artifact derived \
5114         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5115         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5116         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5117         and the `:upgrade-from :from` peers that match against this exact shape; \
5118         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5119         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5120         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5121    )]
5122    VersaoInvalid { versao: String, reason: String },
5123    #[error(
5124        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5125         substrate consumes this string through the shared \
5126         `supervisor::duration_codec` — the same parser routed via `with = \
5127         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5128         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5129         the canonical authoring form is `<integer><unit>` where the unit is one \
5130         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5131         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5132         Without this gate a malformed `:restart-window` silently produced a \
5133         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5134         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5135         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5136         layer with the offending value named verbatim. Omit the slot entirely to \
5137         express \"no reset\"; carry a positive integer duration to express the \
5138         sliding window)"
5139    )]
5140    RestartWindowMalformed {
5141        restart_window: String,
5142        reason: String,
5143    },
5144    #[error(
5145        "{slot} entry is an empty path string — every {slot} entry must name \
5146         a file relative to the caixa root; omit the entry to omit the file \
5147         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5148         itself, so an empty entry silently aliases the project root as a \
5149         declared {slot} file, then fails downstream at parse / existence \
5150         time with a diagnostic that names the root rather than the offending \
5151         entry)"
5152    )]
5153    CodePathEmpty { slot: &'static str },
5154    #[error(
5155        "{slot} entry {} is an absolute path — entries must be relative to \
5156         the caixa root, since `Path::join` replaces the base with an absolute \
5157         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5158         outside the caixa root sandbox; rewrite the entry as a relative path \
5159         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5160         `\"servicos/<name>.computeunit.yaml\"`)",
5161        path.display()
5162    )]
5163    CodePathAbsolute { slot: &'static str, path: PathBuf },
5164    #[error(
5165        "{slot} entry {} contains a `..` component — entries must not traverse \
5166         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5167         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5168         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5169         has no such fence, so a leading `..` escapes unconditionally if the \
5170         resolved target happens to exist)",
5171        path.display()
5172    )]
5173    CodePathParentEscape { slot: &'static str, path: PathBuf },
5174    #[error(
5175        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5176         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5177         loop reads through `tatara_lisp::read` at parse time, so any other \
5178         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5179         structurally a parser error far from the source caixa.lisp, with \
5180         no field naming the offending `:bibliotecas` entry. Pin a relative \
5181         path under the caixa root whose terminating extension is \
5182         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5183         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5184         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5185         (33cc830) axes already carry through the same lifted \
5186         `is_lisp_extension` predicate",
5187        path.display()
5188    )]
5189    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5190    #[error(
5191        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5192         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5193         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5194         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5195         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5196         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5197         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5198         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5199         source caixa.lisp, with no field naming the offending `:servicos` \
5200         entry. Pin a relative path under the caixa root whose terminating \
5201         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5202         `\"servicos/<name>.computeunit.yaml\"`, \
5203         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5204         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5205         on the tatara-lisp-source axis through the peer lifted \
5206         `is_lisp_extension` predicate, here on the compound-suffix axis \
5207         `Path::extension` can't express on its own through the lifted \
5208         `is_computeunit_yaml_extension` predicate",
5209        path.display()
5210    )]
5211    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5212    #[error(
5213        "{slot} entry {} appears more than once (the code-path list is \
5214         a set, not a multiset; every peer Vec-shaped author-supplied \
5215         list past validate is set-not-multiset — `:membros :caixa`, \
5216         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5217         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5218         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5219         code-path lists are the last Vec-shaped author-supplied slots on \
5220         the typed Caixa surface still admitting a duplicate entry. \
5221         `:bibliotecas` duplicates re-parse the same file at \
5222         `feira build` time and silently mask the author's intent to \
5223         declare a *second* biblioteca; `:exe` duplicates collide on the \
5224         flake `packages.<name>` derivation key at the future \
5225         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5226         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5227         rejection far from the source `caixa.lisp`. Drop the duplicate \
5228         or rename it to the actual second file intended)",
5229        path.display()
5230    )]
5231    CodePathDuplicate { slot: &'static str, path: PathBuf },
5232    #[error(
5233        ":etiquetas entry is empty (every tag must carry a non-empty \
5234         registry-search identifier; the empty entry has no operational \
5235         meaning — it indexes nothing in the future caixa-registry search \
5236         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5237         with a no-op tag; omit the entry to express \"no tag on this \
5238         position\")"
5239    )]
5240    EtiquetaEmpty,
5241    #[error(
5242        ":etiquetas entry {etiqueta:?} appears more than once (the \
5243         registry-search tag set is a set, not a multiset; duplicate \
5244         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5245         at chart render — a \"second wins / one silently disappears\" \
5246         shape divergent from every peer typed-graph set gate \
5247         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5248         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5249         duplicate or rename it to the actual tag intended)"
5250    )]
5251    EtiquetaDuplicate { etiqueta: String },
5252    #[error(
5253        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5254         {reason} (the substrate consumes this string through the shared \
5255         `crate::render::is_chart_keyword_shape` predicate — the same \
5256         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5257         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5258         continuation. The canonical authoring shapes are short kebab-case \
5259         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5260         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5261         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5262         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5263         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5264         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5265         `\"mesh,http,grpc\"` — the author meant to author three separate \
5266         list entries; path-separator confusion `\"caixa/servico\"`; \
5267         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5268         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5269         `\"café\"` — every legitimate search tag is strict ASCII; \
5270         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5271         passed `from_lisp` + `validate_etiquetas` + \
5272         `StandardLayout::verify` and landed in the rendered \
5273         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5274         malformed search tag — Artifact Hub's keyword index + the future \
5275         caixa-registry's keyword index would either silently drop the \
5276         tag or fail to index it far from the source caixa.lisp; the gate \
5277         moves the diagnostic to the manifest layer with the offending \
5278         value named verbatim)"
5279    )]
5280    EtiquetaInvalid { etiqueta: String, reason: String },
5281    #[error(
5282        ":autores entry is empty (every maintainer must carry a non-empty \
5283         identifier; the empty entry has no operational meaning — it \
5284         identifies no one in the substrate's authorship index and renders \
5285         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5286         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5287         omit the entry to express \"no maintainer on this position\")"
5288    )]
5289    AutorEmpty,
5290    #[error(
5291        ":autores entry {autor:?} appears more than once (the maintainer \
5292         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5293         `maintainers:` rendering does *no* dedup — duplicate entries \
5294         stack verbatim in `Chart.yaml` as two identical \
5295         `Maintainer {{ name, email: None }}` records, divergent from every \
5296         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5297         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5298         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5299         rename it to the actual author intended)"
5300    )]
5301    AutorDuplicate { autor: String },
5302    #[error(
5303        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5304         {reason} (the substrate consumes this string through the shared \
5305         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5306         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5307         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5308         characters anywhere, Unicode bytes accepted. The canonical authoring \
5309         shapes are short single-line identifiers like `\"pleme-io\"`, \
5310         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5311         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5312         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5313         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5314         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5315         records into one entry instead of splitting into one entry per author; \
5316         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5317         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5318         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5319         `validate_autores` + `StandardLayout::verify` and landed in the \
5320         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5321         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5322         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5323         Artifact Hub maintainer index) would render the maintainer name in a \
5324         single-line column far from the source caixa.lisp; the gate moves the \
5325         diagnostic to the manifest layer with the offending value named \
5326         verbatim)"
5327    )]
5328    AutorInvalid { autor: String, reason: String },
5329    #[error(
5330        ":repositorio is the empty string (every published caixa names its \
5331         git source via a non-empty `:repositorio` locator — the value \
5332         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5333         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5334         `GitRepository.spec.url` via `caixa-flux`'s \
5335         `ClusterBundleOpts::for_caixa`; both consumers' \
5336         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5337         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5338         `url: \"\"` in the rendered artifacts and breaks at `helm \
5339         template` / FluxCD source-controller reconcile time far from the \
5340         source caixa.lisp; omit the slot entirely to defer to the \
5341         renderer's `https://github.com/pleme-io/<nome>` / \
5342         `caixa.nome`-derived fallback, or carry a canonical authoring \
5343         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5344         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5345         `\"file:///path\"`)"
5346    )]
5347    RepositorioEmpty,
5348    #[error(
5349        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5350         (the substrate consumes this string through the shared \
5351         `crate::render::is_git_repo_url` predicate — the same parser the \
5352         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5353         value through via `DepSource::validate`; the canonical authoring \
5354         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5355         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5356         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5357         scp-style SSH form. Without this gate a malformed `:repositorio` \
5358         (whitespace from a paste-from-doc; control characters / CRLF \
5359         from a paste-from-multiline-doc; a leading `-` from a \
5360         CLI-argument-injection footgun; a missing `:` separator from a \
5361         bare `org/repo` shape git treats as a relative filesystem path) \
5362         silently landed in the rendered `Chart.yaml home:` and the \
5363         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5364         FluxCD reconcile time far from the source caixa.lisp; the gate \
5365         moves the diagnostic to the manifest layer with the offending \
5366         value named verbatim)"
5367    )]
5368    RepositorioInvalid { repositorio: String, reason: String },
5369    #[error(
5370        ":descricao is the empty string (every published caixa names \
5371         its purpose via a non-empty `:descricao` summary — the value \
5372         flows verbatim into the rendered `lareira-<nome>` Helm \
5373         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5374         `build_chart_yaml` and into the chart `README.md` header via \
5375         `build_readme`; both consumers' `Option::unwrap_or_else` \
5376         `caixa.nome`-derived fallbacks only fire when the slot is \
5377         `None`, so an empty `Some(\"\")` silently lands as \
5378         `description: \"\"` / a blank `README.md` header in the \
5379         rendered artifacts and breaks at `helm lint` time \
5380         (`WARNING [chart.metadata.description]: description is \
5381         required` on `apiVersion: v2` charts) far from the source \
5382         caixa.lisp; omit the slot entirely to defer to the \
5383         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5384         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5385         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5386         Servico.\"`)"
5387    )]
5388    DescricaoEmpty,
5389    #[error(
5390        ":descricao {descricao:?} is not a valid chart-description shape: \
5391         {reason} (the substrate consumes this string through the shared \
5392         `crate::render::is_chart_description_shape` predicate — the same \
5393         single-line-UTF-8 floor every realistic chart description carries: \
5394         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5395         characters anywhere, Unicode prose bytes accepted. The canonical \
5396         authoring shapes are short single-line summaries like `\"Canonical \
5397         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5398         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5399         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5400         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5401         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5402         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5403         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5404         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5405         `validate_descricao` + `StandardLayout::verify` and landed in the \
5406         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5407         field + `README.md` header paragraph as a YAML-illegal multi-line \
5408         scalar or a silently-trimmed whitespace round-trip — every \
5409         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5410         render the description in a single-line column far from the source \
5411         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5412         with the offending value named verbatim)"
5413    )]
5414    DescricaoInvalid { descricao: String, reason: String },
5415    #[error(
5416        ":licenca is the empty string (every published caixa names \
5417         its license via a non-empty `:licenca` SPDX expression — the \
5418         value flows verbatim into the rendered `lareira-<nome>` Helm \
5419         chart's `README.md` `## License` section via `caixa-helm`'s \
5420         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5421         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5422         only fires when the slot is `None`, so an empty `Some(\"\")` \
5423         silently lands as a bare trailing period in the rendered \
5424         chart `README.md` `License` section far from the source \
5425         caixa.lisp; omit the slot entirely to defer to the \
5426         renderer's `MIT` fallback, or carry a canonical SPDX \
5427         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5428         `\"Apache-2.0 OR MIT\"`)"
5429    )]
5430    LicencaEmpty,
5431    #[error(
5432        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5433         (the substrate consumes this string through the shared \
5434         `crate::render::is_spdx_expression_shape` predicate — the same \
5435         alphabet-floor parser every peer per-axis value-shape gate routes \
5436         its value through; the canonical authoring shapes are single \
5437         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5438         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5439         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5440         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5441         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5442         like `\"LicenseRef-MyLicense\"` / \
5443         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5444         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5445         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5446         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5447         a smart-quote paste; underscore-instead-of-hyphen typo \
5448         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5449         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5450         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5451         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5452         `README.md` `## License` section + a future SPDX-aware \
5453         `Chart.yaml license:` emitter would refuse the value at \
5454         `helm lint` time far from the source caixa.lisp; the gate moves \
5455         the diagnostic to the manifest layer with the offending value \
5456         named verbatim)"
5457    )]
5458    LicencaInvalid { licenca: String, reason: String },
5459    #[error(
5460        ":edicao is the empty string (every published caixa names \
5461         its language edition via a non-empty `:edicao` value — the \
5462         edition determines the tatara-lisp macro surface + \
5463         compatibility flags the substrate applies when building \
5464         the caixa; the canonical `Caixa::template` scaffold every \
5465         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5466         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5467         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5468         construction, so an empty `Some(\"\")` silently lands as a \
5469         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5470         a future renderer-side consumer that folds it through \
5471         `Option::unwrap_or_else` will skip the fallback and pass the \
5472         empty edition through to the substrate's build-time edition \
5473         selector far from the source caixa.lisp; omit the slot \
5474         entirely to defer to the substrate's default edition, or \
5475         carry a canonical edition like `\"2026\"`)"
5476    )]
5477    EdicaoEmpty,
5478    #[error(
5479        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5480         documented tatara-lisp edition is a 4-digit ASCII decimal \
5481         year — `\"2026\"` is the only edition currently minted; \
5482         future-introduced siblings will follow the same shape, peer \
5483         with Cargo's `[package] edition` grammar which every value \
5484         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5485         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5486         paste-from-doc footguns silently passed: a trailing space \
5487         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5488         from a paste-from-multiline-doc, a fullwidth-keyboard \
5489         look-alike (`\"2026\"`), a free-form non-year value \
5490         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5491         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5492         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5493         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5494         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5495         rendered caixa.lisp and broke at the substrate's \
5496         build-time edition selector far from the source caixa.lisp; \
5497         omit the slot entirely to defer to the substrate's default \
5498         edition, or carry a canonical 4-digit ASCII decimal year \
5499         like `\"2026\"`)"
5500    )]
5501    EdicaoInvalid { edicao: String, reason: String },
5502}
5503
5504#[cfg(test)]
5505mod tests {
5506    use super::*;
5507
5508    #[test]
5509    fn template_round_trips() {
5510        let src = Caixa::template("demo");
5511        let c = Caixa::from_lisp(&src).expect("template must parse");
5512        assert_eq!(c.nome, "demo");
5513        assert_eq!(c.versao, "0.1.0");
5514        assert_eq!(c.kind, CaixaKind::Biblioteca);
5515        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5516        assert!(c.deps.is_empty());
5517        assert!(c.deps_dev.is_empty());
5518    }
5519
5520    #[test]
5521    fn register_populates_registry() {
5522        Caixa::register().expect("first register call in this test process must succeed");
5523        let kws = tatara_lisp::domain::registered_keywords();
5524        assert!(kws.contains(&"defcaixa"));
5525    }
5526
5527    #[test]
5528    fn to_lisp_round_trips() {
5529        let src = Caixa::template("demo");
5530        let c1 = Caixa::from_lisp(&src).unwrap();
5531        let emitted = c1.to_lisp();
5532        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
5533        assert_eq!(c1, c2);
5534    }
5535
5536    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
5537    //
5538    // The compounding pin: the variant stores only the typed
5539    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
5540    // (canonical keyword, description, consumer) routes through the enum's
5541    // own accessors at Display time. Prior to that closure the variant
5542    // carried each accessor's return value as a stored `&'static str`
5543    // snapshot alongside `dialeto`; a caller could construct the variant
5544    // with a snapshot that drifted from what `dialeto`'s accessors would
5545    // return, and every downstream user-facing projection would silently
5546    // disagree with the classification. Storing only the axis makes the
5547    // drift structurally impossible.
5548
5549    #[test]
5550    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
5551        // Single-field construction is the whole compounding shape — a
5552        // future re-introduction of a snapshot field (a `palavra_canonica:
5553        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
5554        // would re-open the drift surface and this construction would fail
5555        // to compile with "missing field" until every snapshot was seeded
5556        // at the call site again. The compile-time guarantee is the
5557        // invariant; the assertion below only witnesses that the
5558        // construction is well-formed after the closure.
5559        let err = LeituraError::DialetoEstrangeiro {
5560            dialeto: crate::dialeto::CaixaDialeto::Molde,
5561        };
5562        assert!(matches!(
5563            err,
5564            LeituraError::DialetoEstrangeiro {
5565                dialeto: crate::dialeto::CaixaDialeto::Molde,
5566            }
5567        ));
5568    }
5569
5570    #[test]
5571    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
5572        // For every foreign-dialect classification the variant surfaces —
5573        // [`crate::dialeto::CaixaDialeto::Molde`] and
5574        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
5575        // variants [`Caixa::from_lisp`] raises this error for — the
5576        // rendered [`std::fmt::Display`] byte-string must interpolate each
5577        // typed accessor's return verbatim. A future re-introduction of a
5578        // stored `&'static str` snapshot alongside `dialeto` that Display
5579        // read instead of the accessor would fail this pin as soon as the
5580        // two disagreed; a future accessor rebrand (a per-dialect
5581        // consumer rename, a canonical-keyword shift once the substrate
5582        // migration named in [`crate::dialeto`] completes) reaches every
5583        // consumer through one typed dispatch and this pin verifies the
5584        // display path is one of them.
5585        for d in [
5586            crate::dialeto::CaixaDialeto::Molde,
5587            crate::dialeto::CaixaDialeto::MoldePosicional,
5588        ] {
5589            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
5590            assert!(
5591                rendered.contains(d.palavra_canonica()),
5592                "Display must interpolate `dialeto.palavra_canonica()` \
5593                 verbatim — a stored snapshot would silently drift from \
5594                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
5595            );
5596            assert!(
5597                rendered.contains(d.descricao()),
5598                "Display must interpolate `dialeto.descricao()` verbatim. \
5599                 dialect: {d}, rendered: {rendered:?}"
5600            );
5601            assert!(
5602                rendered.contains(d.consumidor()),
5603                "Display must interpolate `dialeto.consumidor()` verbatim. \
5604                 dialect: {d}, rendered: {rendered:?}"
5605            );
5606        }
5607    }
5608
5609    #[test]
5610    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
5611        // The end-to-end pin the compounding closure defends: a
5612        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
5613        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
5614        // rendered Display byte-string names the Molde accessors'
5615        // returns verbatim. Any future path that constructed the variant
5616        // with a mismatched snapshot (a stored `palavra_canonica:
5617        // "defcaixa"` on a `Molde` classification) would land Display
5618        // pointing at `defcaixa` while the typed axis said `Molde` — the
5619        // exact drift the closure removes.
5620        let src = r#"
5621          (defcaixa
5622            :name "x"
5623            :kind :Biblioteca
5624            :ecosystem :rust-single-crate
5625            :package {:name "x" :version "0.1.0"})
5626        "#;
5627        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
5628        match err {
5629            LeituraError::DialetoEstrangeiro { dialeto } => {
5630                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
5631                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5632                assert!(rendered.contains(dialeto.palavra_canonica()));
5633                assert!(rendered.contains(dialeto.consumidor()));
5634                assert!(rendered.contains(dialeto.descricao()));
5635            }
5636            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5637        }
5638    }
5639
5640    #[test]
5641    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
5642        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5643        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
5644        // positional-arity `defmolde` form written under a `(defcaixa …)`
5645        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
5646        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
5647        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
5648        // so no test exercised the positional-arity path through
5649        // `Caixa::from_lisp` specifically; the sibling
5650        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
5651        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
5652        // two arms route through the lifted
5653        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5654        // typed predicate — the same predicate the pre-lift `foreign =>`
5655        // wildcard resolved to today — and this pin makes the
5656        // positional-arity arm's byte-shape at the gate explicit rather
5657        // than implied by wildcard-absorption. A future regression that
5658        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
5659        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
5660        // from the two-arity closure) would fail this pin at caixa-core
5661        // test time rather than surfacing far from the change as a
5662        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
5663        // …)` silently parsing past the derive.
5664        let src = r#"
5665          (defcaixa todoku-go
5666            :kind :Biblioteca
5667            :ecosystem :go
5668            :package {:name "todoku-go" :version "0.3.0"})
5669        "#;
5670        let err =
5671            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
5672        match err {
5673            LeituraError::DialetoEstrangeiro { dialeto } => {
5674                assert_eq!(
5675                    dialeto,
5676                    crate::dialeto::CaixaDialeto::MoldePosicional,
5677                    "DialetoEstrangeiro must carry the MoldePosicional \
5678                     variant verbatim — the positional-arity `defmolde` \
5679                     form under a `(defcaixa …)` head is the \
5680                     `MoldePosicional` arm's canonical byte-shape"
5681                );
5682                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
5683                assert!(
5684                    rendered.contains(dialeto.palavra_canonica()),
5685                    "Display must interpolate `dialeto.palavra_canonica()` \
5686                     verbatim on the MoldePosicional arm; rendered: \
5687                     {rendered:?}"
5688                );
5689                assert!(
5690                    rendered.contains(dialeto.consumidor()),
5691                    "Display must interpolate `dialeto.consumidor()` \
5692                     verbatim on the MoldePosicional arm; rendered: \
5693                     {rendered:?}"
5694                );
5695                assert!(
5696                    rendered.contains(dialeto.descricao()),
5697                    "Display must interpolate `dialeto.descricao()` \
5698                     verbatim on the MoldePosicional arm; rendered: \
5699                     {rendered:?}"
5700                );
5701            }
5702            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
5703        }
5704    }
5705
5706    #[test]
5707    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
5708        // Load-bearing byte-parity pin: for every arm in
5709        // [`crate::dialeto::CaixaDialeto::ALL`], the
5710        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
5711        // partition must agree with the lifted
5712        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
5713        // typed predicate — i.e. from_lisp raises
5714        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
5715        // `d.is_molde_family()` returns `true`, and does NOT raise
5716        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
5717        // predicate returns `false` (the arm's source falls through to
5718        // the derive — parses cleanly on
5719        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
5720        // [`LeituraError::Leitura`] on
5721        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
5722        //
5723        // Pre-lift the gate hand-rolled a three-arm match
5724        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
5725        // whose `foreign =>` wildcard expressed no compile-time link
5726        // back to the substrate primitive's arm-family; a future fifth
5727        // dialect the [`crate::dialeto`] module doc's "third dialect"
5728        // hazard actualises would fall silently onto the wildcard
5729        // regardless of whether it belonged to the `defmolde` family or
5730        // to a distinct `defcaixa`-family. Post-lift the partition
5731        // resolves through
5732        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
5733        // typed dispatch, and this pin refuses any future regression
5734        // that silently split the from_lisp partition from the typed
5735        // predicate — the two paths now migrate as one on any future
5736        // arm addition.
5737        //
5738        // Sibling in shape to the peer
5739        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
5740        // (e9d2315) that pins the same byte-parity between
5741        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
5742        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
5743        // `== "defmolde"` classifier — extends the discipline from the
5744        // two paths within the [`crate::dialeto`] primitive onto the
5745        // third external consumer of the `defmolde`-family partition
5746        // (the [`Caixa::from_lisp`] gate that raises
5747        // [`LeituraError::DialetoEstrangeiro`]).
5748        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
5749            (
5750                crate::dialeto::CaixaDialeto::Pacote,
5751                r#"
5752                  (defcaixa
5753                    :nome   "checkout"
5754                    :versao "0.1.0"
5755                    :kind   Biblioteca
5756                    :edicao "2026"
5757                    :descricao "canonical Pacote source"
5758                    :autores ()
5759                    :etiquetas ()
5760                    :deps ()
5761                    :deps-dev ()
5762                    :bibliotecas ("lib/checkout.lisp"))
5763                "#,
5764            ),
5765            (
5766                crate::dialeto::CaixaDialeto::Molde,
5767                r#"
5768                  (defcaixa
5769                    :name "base64"
5770                    :kind :Biblioteca
5771                    :ecosystem :rust-single-crate
5772                    :package {:name "base64" :version "0.22.1"}
5773                    :workflows [:auto-release])
5774                "#,
5775            ),
5776            (
5777                crate::dialeto::CaixaDialeto::MoldePosicional,
5778                r#"
5779                  (defcaixa todoku-go
5780                    :kind :Biblioteca
5781                    :ecosystem :go
5782                    :package {:name "todoku-go" :version "0.3.0"})
5783                "#,
5784            ),
5785            (
5786                crate::dialeto::CaixaDialeto::Desconhecido,
5787                r#"(defcaixa :licenca "MIT")"#,
5788            ),
5789        ];
5790
5791        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
5792        // must appear in the fixture table so the pin's arm-set stays
5793        // synchronised with the enum's arm-set. Fails at test time if a
5794        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
5795        // (with a corresponding `is_molde_family` return) forgot to
5796        // extend this fixture table with a canonical source for the new
5797        // arm — the pin cannot cover an arm it has no source for.
5798        for &expected in crate::dialeto::CaixaDialeto::ALL {
5799            assert!(
5800                fixtures.iter().any(|(d, _)| *d == expected),
5801                "fixture table must carry a canonical source for every \
5802                 CaixaDialeto arm; missing: {expected:?}"
5803            );
5804        }
5805
5806        for &(expected_dialect, src) in fixtures {
5807            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
5808                panic!(
5809                    "fixture source for {expected_dialect:?} must classify \
5810                     cleanly, got err: {err:?}"
5811                )
5812            });
5813            assert_eq!(
5814                classified, expected_dialect,
5815                "fixture source for {expected_dialect:?} must classify as \
5816                 {expected_dialect:?} (drift here defeats the byte-parity \
5817                 pin below — a source labelled for one arm but classifying \
5818                 as another would silently satisfy or violate the pin for \
5819                 the wrong reason)"
5820            );
5821
5822            let outcome = Caixa::from_lisp(src);
5823            match (expected_dialect.is_molde_family(), &outcome) {
5824                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
5825                    assert_eq!(
5826                        *dialeto, expected_dialect,
5827                        "DialetoEstrangeiro must carry the same typed arm \
5828                         the classifier returned — a drift here would let \
5829                         from_lisp raise the error while pointing at the \
5830                         wrong dialect (e.g. rejecting a \
5831                         MoldePosicional source as Molde). arm: \
5832                         {expected_dialect:?}"
5833                    );
5834                }
5835                (true, other) => panic!(
5836                    "arm {expected_dialect:?} has is_molde_family() = true \
5837                     so from_lisp must raise DialetoEstrangeiro carrying \
5838                     {expected_dialect:?}; got: {other:?}"
5839                ),
5840                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
5841                    "arm {expected_dialect:?} has is_molde_family() = false \
5842                     so from_lisp must NOT raise DialetoEstrangeiro; got \
5843                     one carrying: {dialeto:?}. This means the typed \
5844                     predicate and the from_lisp partition disagree on \
5845                     this arm — exactly the drift this pin refuses."
5846                ),
5847                (false, _) => {
5848                    // A non-molde arm's source falls through to the
5849                    // derive: Pacote sources parse to Ok(_); Desconhecido
5850                    // sources surface as LeituraError::Leitura from the
5851                    // derive's own unknown-keyword rejection. Either
5852                    // shape is acceptable here — the pin's promise is
5853                    // narrower: "no DialetoEstrangeiro on
5854                    // is_molde_family() == false".
5855                }
5856            }
5857        }
5858    }
5859
5860    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
5861
5862    #[test]
5863    fn limits_round_trip_via_json() {
5864        use crate::LimitsSpec;
5865        use std::time::Duration;
5866        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5867        c.limits = Some(LimitsSpec {
5868            memory: Some(64 * 1024 * 1024),
5869            fuel: Some(1_000_000),
5870            wall_clock: Some(Duration::from_secs(30)),
5871            cpu: Some(500),
5872        });
5873        let json = serde_json::to_string(&c).unwrap();
5874        assert!(json.contains("\"limits\""));
5875        assert!(json.contains("\"64MiB\""));
5876        assert!(json.contains("\"30s\""));
5877        assert!(json.contains("\"500m\""));
5878        let back: Caixa = serde_json::from_str(&json).unwrap();
5879        assert_eq!(c.limits, back.limits);
5880    }
5881
5882    #[test]
5883    fn behavior_round_trip_via_json() {
5884        use crate::BehaviorSpec;
5885        use std::path::PathBuf;
5886        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5887        c.behavior = Some(BehaviorSpec {
5888            on_init: Some(PathBuf::from("lib/init.lisp")),
5889            on_call: Some(PathBuf::from("lib/handlers.lisp")),
5890            ..Default::default()
5891        });
5892        let json = serde_json::to_string(&c).unwrap();
5893        let back: Caixa = serde_json::from_str(&json).unwrap();
5894        assert_eq!(c.behavior, back.behavior);
5895    }
5896
5897    #[test]
5898    fn upgrade_from_round_trip_via_json() {
5899        use crate::{UpgradeFromEntry, UpgradeInstruction};
5900        use std::path::PathBuf;
5901        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5902        c.upgrade_from = vec![UpgradeFromEntry {
5903            from: "0.1.0".into(),
5904            instructions: vec![
5905                UpgradeInstruction::LoadModule {
5906                    module: "demo".into(),
5907                },
5908                UpgradeInstruction::StateChange {
5909                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
5910                },
5911                UpgradeInstruction::SoftPurge {
5912                    module: "demo-old".into(),
5913                },
5914            ],
5915        }];
5916        let json = serde_json::to_string(&c).unwrap();
5917        let back: Caixa = serde_json::from_str(&json).unwrap();
5918        assert_eq!(c.upgrade_from, back.upgrade_from);
5919    }
5920
5921    #[test]
5922    fn supervisor_view_returns_typed_shape() {
5923        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
5924        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
5925        c.kind = CaixaKind::Supervisor;
5926        c.bibliotecas.clear();
5927        c.estrategia = Some(RestartStrategy::OneForOne);
5928        c.max_restarts = Some(5);
5929        c.restart_window = Some("60s".into());
5930        c.children = vec![ChildSpec {
5931            caixa: "worker".into(),
5932            versao: "^0.1".into(),
5933            restart: RestartPolicy::Permanent,
5934        }];
5935        let view = c.supervisor_view().expect("Supervisor kind has a view");
5936        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
5937        assert_eq!(view.max_restarts, 5);
5938        assert_eq!(
5939            view.restart_window,
5940            Some(std::time::Duration::from_secs(60))
5941        );
5942        assert_eq!(view.children.len(), 1);
5943        view.validate().unwrap();
5944    }
5945
5946    #[test]
5947    fn supervisor_view_none_for_non_supervisor_kinds() {
5948        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5949        assert!(c.supervisor_view().is_none());
5950    }
5951
5952    #[test]
5953    fn declared_mesh_slots_empty_for_bare_caixa() {
5954        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5955        assert!(c.declared_mesh_slots().is_empty());
5956    }
5957
5958    #[test]
5959    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
5960        use crate::{Entrada, Membro};
5961        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
5962        // Set a non-adjacent pair (:membros + :entrada) to pin that the
5963        // canonical declaration order is preserved regardless of which
5964        // subset is populated.
5965        c.membros = vec![Membro {
5966            caixa: "a".into(),
5967            versao: "^0.1".into(),
5968        }];
5969        c.entrada = Some(Entrada {
5970            host: "x.example.com".into(),
5971            para: "a".into(),
5972            paths: vec![],
5973            port: 8080,
5974        });
5975        assert_eq!(
5976            c.declared_mesh_slots(),
5977            vec![
5978                crate::render::M3_AUTHOR_KEY_MEMBROS,
5979                crate::render::M3_AUTHOR_KEY_ENTRADA,
5980            ]
5981        );
5982    }
5983
5984    #[test]
5985    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
5986        // Scalar-value pin: the five author-facing kebab-case labels the
5987        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
5988        // mesh slot axis, one arm per typed slot. Mirrors the peer
5989        // scalar-value pin the sibling
5990        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
5991        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
5992        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
5993        // carry (f49c8b0), so both altitudes of the typed-slot algebra
5994        // (per-Servico M2 + per-Aplicacao M3) share the same
5995        // "one canonical byte-string per arm" discipline. A future
5996        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
5997        // `:politicas` → `:policies`, `:placement` → `:distribution`,
5998        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
5999        // and every consumer that reaches for the label picks it up at
6000        // build time rather than at runtime as a downstream mismatch.
6001        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
6002        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
6003        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
6004        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
6005        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
6006    }
6007
6008    #[test]
6009    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
6010        // Production-through-const pin: the five per-arm labels the
6011        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
6012        // `Vec` route through the lifted
6013        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
6014        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
6015        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
6016        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
6017        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
6018        // declaration order. A future re-order or drift at the tagger
6019        // (a rename that reaches the tagger but not the const, or vice
6020        // versa) surfaces here at build time rather than at runtime as
6021        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6022        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6023        // commit. Mirror of the peer
6024        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6025        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
6026        // axis.
6027        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
6028        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6029        c.membros = vec![Membro {
6030            caixa: "a".into(),
6031            versao: "^0.1".into(),
6032        }];
6033        c.contratos = vec![WitContract {
6034            de: "a".into(),
6035            para: "a".into(),
6036            wit: "wasi:http/proxy".into(),
6037            endpoint: Some("/x".into()),
6038            subject: None,
6039            slot: None,
6040        }];
6041        c.politicas = Some(MeshPolicy::default());
6042        c.placement = Some(Placement {
6043            estrategia: PlacementStrategy::Replicated,
6044            clusters: vec!["rio".into()],
6045            affinity: None,
6046            shard_key: None,
6047        });
6048        c.entrada = Some(Entrada {
6049            host: "x.example.com".into(),
6050            para: "a".into(),
6051            paths: vec![],
6052            port: 8080,
6053        });
6054        assert_eq!(
6055            c.declared_mesh_slots(),
6056            vec![
6057                crate::render::M3_AUTHOR_KEY_MEMBROS,
6058                crate::render::M3_AUTHOR_KEY_CONTRATOS,
6059                crate::render::M3_AUTHOR_KEY_POLITICAS,
6060                crate::render::M3_AUTHOR_KEY_PLACEMENT,
6061                crate::render::M3_AUTHOR_KEY_ENTRADA,
6062            ]
6063        );
6064    }
6065
6066    #[test]
6067    fn declared_supervisor_slots_empty_for_bare_caixa() {
6068        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6069        assert!(c.declared_supervisor_slots().is_empty());
6070    }
6071
6072    #[test]
6073    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
6074        use crate::RestartStrategy;
6075        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6076        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
6077        // that the canonical declaration order is preserved regardless
6078        // of which subset is populated.
6079        c.estrategia = Some(RestartStrategy::OneForOne);
6080        c.restart_window = Some("60s".into());
6081        assert_eq!(
6082            c.declared_supervisor_slots(),
6083            vec![
6084                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6085                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6086            ]
6087        );
6088    }
6089
6090    #[test]
6091    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6092        // Scalar-value pin: the four author-facing kebab-case labels the
6093        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
6094        // supervision-tree slot axis, one arm per typed slot. Mirrors the
6095        // peer scalar-value pins the sibling
6096        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
6097        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
6098        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
6099        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
6100        // top-level M3 slot consts carry, so all three kind-scoped
6101        // typed-slot-family author-facing-label axes route through one
6102        // canonical per-arm declaration. A future rebrand
6103        // (`:estrategia` → `:strategy` for English uniformity,
6104        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
6105        // `MaxIntensity` name, `:restart-window` → `:period` matching
6106        // OTP's `Period` name, `:children` → `:workers` matching Elixir
6107        // idiom) lands as an edit to exactly one const, and every
6108        // consumer that reaches for the label picks it up at build time
6109        // rather than at runtime as a downstream mismatch.
6110        assert_eq!(
6111            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6112            ":estrategia"
6113        );
6114        assert_eq!(
6115            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6116            ":max-restarts"
6117        );
6118        assert_eq!(
6119            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6120            ":restart-window"
6121        );
6122        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
6123    }
6124
6125    #[test]
6126    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
6127        // Production-through-const pin: the four per-arm labels the
6128        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
6129        // return `Vec` route through the lifted
6130        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
6131        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
6132        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
6133        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
6134        // canonical declaration order. A future re-order or drift at the
6135        // tagger (a rename that reaches the tagger but not the const, or
6136        // vice versa) surfaces here at build time rather than at runtime
6137        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
6138        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6139        // commit. Mirror of the peer
6140        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6141        // (f49c8b0) and
6142        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
6143        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
6144        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6145        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6146        c.estrategia = Some(RestartStrategy::OneForOne);
6147        c.max_restarts = Some(5);
6148        c.restart_window = Some("60s".into());
6149        c.children = vec![ChildSpec {
6150            caixa: "worker".into(),
6151            versao: "^0.1".into(),
6152            restart: RestartPolicy::Permanent,
6153        }];
6154        assert_eq!(
6155            c.declared_supervisor_slots(),
6156            vec![
6157                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
6158                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
6159                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
6160                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
6161            ]
6162        );
6163    }
6164
6165    #[test]
6166    fn declared_servico_slots_empty_for_bare_caixa() {
6167        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6168        assert!(c.declared_servico_slots().is_empty());
6169    }
6170
6171    #[test]
6172    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
6173        use crate::{UpgradeFromEntry, UpgradeInstruction};
6174        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6175        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
6176        // the canonical declaration order is preserved regardless of
6177        // which subset is populated.
6178        c.limits = Some(crate::LimitsSpec {
6179            fuel: Some(1_000_000),
6180            ..Default::default()
6181        });
6182        c.upgrade_from = vec![UpgradeFromEntry {
6183            from: "0.1.0".into(),
6184            instructions: vec![UpgradeInstruction::Restart],
6185        }];
6186        assert_eq!(
6187            c.declared_servico_slots(),
6188            vec![
6189                crate::render::M2_AUTHOR_KEY_LIMITS,
6190                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6191            ]
6192        );
6193    }
6194
6195    #[test]
6196    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6197        // Scalar-value pin: the three author-facing kebab-case labels
6198        // the `(defcaixa … :<slot> (…))` surface admits on the M2
6199        // top-level slot axis, one arm per typed slot. Mirrors the peer
6200        // scalar-value pin the sibling renderer-side
6201        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
6202        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
6203        // consts carry, so both halves of the M2 top-level slot dual
6204        // axis (author-facing kebab-case label + renderer-side
6205        // camelCase overlay-container wire key) route through one
6206        // canonical per-arm declaration. A future rebrand
6207        // (`:limits` → `:sandbox` matching Lunatic per-process
6208        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
6209        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
6210        // matching Erlang's verbatim appup name) lands as an edit to
6211        // exactly one const, and every consumer that reaches for the
6212        // label picks it up at build time rather than at runtime as a
6213        // downstream mismatch.
6214        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
6215        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
6216        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
6217    }
6218
6219    #[test]
6220    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
6221        // Production-through-const pin: the three per-arm labels the
6222        // [`Caixa::declared_servico_slots`] tagger pushes onto its
6223        // return `Vec` route through the lifted
6224        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6225        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6226        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
6227        // declaration order. A future re-order or drift at the tagger
6228        // (a rename that reaches the tagger but not the const, or vice
6229        // versa) surfaces here at build time rather than at runtime as
6230        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
6231        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6232        // commit. Mirror of the peer
6233        // [`crate::behavior::BehaviorSpec::declared_slots`] production
6234        // tagger pin (889dc18) on the sibling per-callback axis.
6235        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
6236        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6237        c.limits = Some(crate::LimitsSpec {
6238            fuel: Some(1_000_000),
6239            ..Default::default()
6240        });
6241        c.behavior = Some(BehaviorSpec {
6242            on_init: Some(PathBuf::from("lib/init.lisp")),
6243            ..Default::default()
6244        });
6245        c.upgrade_from = vec![UpgradeFromEntry {
6246            from: "0.1.0".into(),
6247            instructions: vec![UpgradeInstruction::Restart],
6248        }];
6249        assert_eq!(
6250            c.declared_servico_slots(),
6251            vec![
6252                crate::render::M2_AUTHOR_KEY_LIMITS,
6253                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
6254                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
6255            ]
6256        );
6257    }
6258
6259    #[test]
6260    fn existing_manifests_unaffected_by_new_optional_slots() {
6261        // Regression test: a caixa.lisp authored before M2 typed slots
6262        // should still parse + serialize cleanly. The bare `defcaixa`
6263        // emitted by `Caixa::template` has none of the new fields.
6264        let src = Caixa::template("legacy");
6265        let c = Caixa::from_lisp(&src).unwrap();
6266        assert!(c.limits.is_none());
6267        assert!(c.behavior.is_none());
6268        assert!(c.upgrade_from.is_empty());
6269        assert!(c.estrategia.is_none());
6270        assert!(c.children.is_empty());
6271
6272        // And to_lisp emits a manifest with the new slots in the
6273        // empty/default state — round-trippable.
6274        let emitted = c.to_lisp();
6275        let back = Caixa::from_lisp(&emitted).unwrap();
6276        assert_eq!(c, back);
6277    }
6278
6279    #[test]
6280    fn validate_deps_accepts_canonical_caixa() {
6281        // Positive control: the bare template — zero deps, zero
6282        // deps_dev — passes the gate trivially. A future axis added to
6283        // `Dep::validate` mustn't regress an empty-deps caixa to a
6284        // build error.
6285        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6286        c.validate_deps().unwrap();
6287    }
6288
6289    #[test]
6290    fn validate_deps_rejects_invalid_versao_in_deps() {
6291        // Fail-before-pass-after pin: a malformed `:deps :versao`
6292        // surfaces at validate_deps() time, not at lacre-resolve time.
6293        // Mirrors `rejects_invalid_membro_versao_requirement` and
6294        // `validate_rejects_invalid_child_versao_requirement` on the
6295        // other two `:versao` axes.
6296        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6297        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
6298        let err = c.validate_deps().unwrap_err();
6299        assert!(
6300            matches!(
6301                err,
6302                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6303                    if nome == "caixa-teia" && versao == "^bad-version"
6304            ),
6305            "got {err:?}"
6306        );
6307    }
6308
6309    #[test]
6310    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
6311        // Parity pin: `:deps-dev` must run through the same per-entry
6312        // validator as `:deps` — a typo in either axis surfaces the
6313        // same diagnostic. Without this leg, `:deps-dev` would be a
6314        // second-class citizen of the typed surface and an author
6315        // could land a build that passes validate_deps but fails at
6316        // `feira lock`-time when the dev-dep is resolved for a test
6317        // build.
6318        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6319        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
6320        let err = c.validate_deps().unwrap_err();
6321        assert!(
6322            matches!(
6323                err,
6324                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6325                    if nome == "tatara-check" && versao == "^^0.1"
6326            ),
6327            "got {err:?}"
6328        );
6329    }
6330
6331    #[test]
6332    fn validate_deps_runs_deps_before_deps_dev() {
6333        // Order pin: when both lists carry typos, the `:deps`
6334        // diagnostic surfaces first. The author's mental model is
6335        // "runtime deps are load-bearing; dev deps are scaffolding";
6336        // surfacing the runtime axis first matches that hierarchy.
6337        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6338        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
6339        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
6340        let err = c.validate_deps().unwrap_err();
6341        assert!(
6342            matches!(
6343                err,
6344                crate::dep::DepError::VersaoInvalid { ref nome, .. }
6345                    if nome == "runtime-dep"
6346            ),
6347            "expected `:deps` typo to surface first, got {err:?}"
6348        );
6349    }
6350
6351    #[test]
6352    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
6353        // Positive control sweep across both lists. Pin every
6354        // canonical Cargo-shaped form so a future tightening of the
6355        // accepted set surfaces here as a test failure (parity with
6356        // `accepts_canonical_membro_versao_forms` and
6357        // `validate_accepts_canonical_child_versao_forms`).
6358        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6359        c.deps = vec![
6360            Dep::simple("caret", "^0.1"),
6361            Dep::simple("tilde", "~0.1.2"),
6362            Dep::simple("exact", "0.1.0"),
6363            Dep::simple("wildcard", "*"),
6364            Dep::simple("multi-range", ">=0.1, <2"),
6365        ];
6366        c.deps_dev = vec![
6367            Dep::simple("dev-caret", "^0.1"),
6368            Dep::simple("dev-wildcard", "*"),
6369        ];
6370        c.validate_deps().unwrap();
6371    }
6372
6373    #[test]
6374    fn validate_deps_diagnostic_carries_offending_dep() {
6375        // Diagnostic-shape pin: the error names the offending entry's
6376        // `:nome` + `:versao` verbatim and carries a non-empty
6377        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
6378        // run can render the diagnostic without re-parsing.
6379        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6380        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
6381        let err = c.validate_deps().unwrap_err();
6382        let crate::dep::DepError::VersaoInvalid {
6383            nome,
6384            versao,
6385            reason,
6386        } = err
6387        else {
6388            panic!("expected VersaoInvalid, got other variant");
6389        };
6390        assert_eq!(nome, "caixa-teia");
6391        assert_eq!(versao, "not-a-req");
6392        assert!(
6393            !reason.is_empty(),
6394            "VersaoInvalid `reason` must carry the parser's wording verbatim"
6395        );
6396    }
6397
6398    #[test]
6399    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
6400        // Cross-axis pin: `validate_deps` walks both :deps and
6401        // :deps-dev through `Dep::validate`, and the new fonte gate
6402        // (`:tag` + `:branch` both set — the canonical "pin drift"
6403        // footgun) must surface from the :deps-dev arm with the
6404        // offending entry's :nome named. Pin the :deps-dev arm
6405        // explicitly so a future shortcut that only walks :deps
6406        // surfaces here as a regression.
6407        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6408        c.deps_dev = vec![Dep {
6409            nome: "dev-only".into(),
6410            versao: "^0.1".into(),
6411            fonte: Some(crate::DepSource::Git {
6412                repo: "github:p/x".into(),
6413                tag: Some("v1".into()),
6414                rev: None,
6415                branch: Some("main".into()),
6416            }),
6417            opcional: false,
6418            caracteristicas: vec![],
6419        }];
6420        let err = c.validate_deps().unwrap_err();
6421        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
6422            panic!("expected FontePinAmbiguous from :deps-dev walk");
6423        };
6424        assert_eq!(nome, "dev-only");
6425        assert!(pins.contains(":tag") && pins.contains(":branch"));
6426    }
6427
6428    #[test]
6429    fn validate_deps_rejects_empty_repo_in_deps() {
6430        // Parity pin on the :deps arm: an empty :repo on the runtime
6431        // deps list surfaces the same FonteRepoEmpty diagnostic the
6432        // dep.rs per-entry tests pin, naming the offending entry.
6433        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6434        c.deps = vec![Dep {
6435            nome: "runtime".into(),
6436            versao: "^0.1".into(),
6437            fonte: Some(crate::DepSource::Git {
6438                repo: String::new(),
6439                tag: Some("v1".into()),
6440                rev: None,
6441                branch: None,
6442            }),
6443            opcional: false,
6444            caracteristicas: vec![],
6445        }];
6446        let err = c.validate_deps().unwrap_err();
6447        assert!(
6448            matches!(
6449                err,
6450                crate::dep::DepError::FonteRepoEmpty { ref nome }
6451                    if nome == "runtime"
6452            ),
6453            "got {err:?}"
6454        );
6455    }
6456
6457    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
6458
6459    #[test]
6460    fn validate_deps_rejects_duplicate_nome_in_deps() {
6461        // Fail-before-pass-after pin: two `:deps` entries naming the same
6462        // caixa carry two `:versao` / `:fonte` / feature triples that the
6463        // caixa-resolver's lacre pipeline collapses (the second silently
6464        // overwrites the first at `concrete_versao`-resolve time). The
6465        // gate surfaces the duplicate at validate-time, naming the
6466        // offending caixa + the list, before the resolver-side silent
6467        // drop. Mirrors the peer typed-graph duplicate gates
6468        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
6469        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6470        c.deps = vec![
6471            Dep::simple("caixa-teia", "^0.1"),
6472            Dep::simple("caixa-teia", "^0.2"),
6473        ];
6474        let err = c.validate_deps().unwrap_err();
6475        assert!(
6476            matches!(
6477                err,
6478                crate::dep::DepError::DuplicateNome { ref nome, list }
6479                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6480            ),
6481            "got {err:?}"
6482        );
6483    }
6484
6485    #[test]
6486    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
6487        // Parity pin: `:deps-dev` runs through the same per-list
6488        // duplicate check as `:deps` — neither axis is a second-class
6489        // citizen of the set-not-multiset discipline.
6490        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6491        c.deps_dev = vec![
6492            Dep::simple("tatara-check", "*"),
6493            Dep::simple("tatara-check", "^0.1"),
6494        ];
6495        let err = c.validate_deps().unwrap_err();
6496        assert!(
6497            matches!(
6498                err,
6499                crate::dep::DepError::DuplicateNome { ref nome, list }
6500                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
6501            ),
6502            "got {err:?}"
6503        );
6504    }
6505
6506    #[test]
6507    fn validate_deps_accepts_cross_list_same_nome() {
6508        // The Cargo `[dependencies]` + `[dev-dependencies]` override
6509        // convention is preserved: a name appearing in *both* lists is
6510        // valid (the dev-pin overrides at test/dev time). Only
6511        // within-list duplicates are structurally incoherent — pin the
6512        // permissive cross-list semantics so a future shortcut that
6513        // collapses the two seen-sets into one surfaces here as a test
6514        // failure.
6515        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6516        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
6517        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
6518        c.validate_deps().unwrap();
6519    }
6520
6521    #[test]
6522    fn validate_deps_accepts_distinct_nome_in_both_lists() {
6523        // Positive control: distinct names within each list pass — the
6524        // gate's identity element on the canonical authoring shape.
6525        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6526        c.deps = vec![
6527            Dep::simple("caixa-teia", "^0.1"),
6528            Dep::simple("pleme-mesh", "*"),
6529        ];
6530        c.deps_dev = vec![
6531            Dep::simple("tatara-check", "*"),
6532            Dep::simple("dev-shim", "^0.1"),
6533        ];
6534        c.validate_deps().unwrap();
6535    }
6536
6537    #[test]
6538    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
6539        // Diagnostic-precedence pin: a malformed `:versao` on the
6540        // duplicating entry surfaces its narrower `VersaoInvalid`
6541        // diagnostic first, before the cross-entry duplicate gate fires
6542        // — the canonical "per-entry shape before cross-entry uniqueness"
6543        // precedence every peer set-not-multiset gate establishes
6544        // (`*_invalid_fires_before_duplicate_check` pins on
6545        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
6546        // `validate_upgrade_from`).
6547        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6548        c.deps = vec![
6549            Dep::simple("caixa-teia", "^0.1"),
6550            Dep::simple("caixa-teia", "^bad-version"),
6551        ];
6552        let err = c.validate_deps().unwrap_err();
6553        assert!(
6554            matches!(
6555                err,
6556                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
6557                    if nome == "caixa-teia" && versao == "^bad-version"
6558            ),
6559            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
6560        );
6561    }
6562
6563    #[test]
6564    fn validate_deps_duplicate_diagnostic_names_first_collision() {
6565        // First-collision determinism pin: with three entries naming the
6566        // same caixa, the first colliding pair surfaces — not the last.
6567        // Mirrors the peer first-collision posture on every
6568        // duplicate-target gate
6569        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
6570        // — the second entry is the first collision; this gate uses the
6571        // same shape: the second entry's `:nome` lands in the diagnostic
6572        // because `seen.insert(first.nome)` already populated the set).
6573        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6574        c.deps = vec![
6575            Dep::simple("caixa-teia", "^0.1"),
6576            Dep::simple("caixa-teia", "^0.2"),
6577            Dep::simple("caixa-teia", "^0.3"),
6578        ];
6579        let err = c.validate_deps().unwrap_err();
6580        // The diagnostic carries the offending caixa name; the
6581        // implementation surfaces on the *second* entry (the first
6582        // collision), so the test pins the `:nome` value.
6583        assert!(
6584            matches!(
6585                err,
6586                crate::dep::DepError::DuplicateNome { ref nome, list }
6587                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6588            ),
6589            "got {err:?}"
6590        );
6591    }
6592
6593    #[test]
6594    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
6595        // Cross-list precedence pin: when both lists carry duplicates,
6596        // the `:deps` diagnostic surfaces first — same author-mental-
6597        // model ordering the `validate_deps_runs_deps_before_deps_dev`
6598        // pin establishes for malformed `:versao` (runtime axis before
6599        // dev axis).
6600        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6601        c.deps = vec![
6602            Dep::simple("runtime-dep", "^0.1"),
6603            Dep::simple("runtime-dep", "^0.2"),
6604        ];
6605        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
6606        let err = c.validate_deps().unwrap_err();
6607        assert!(
6608            matches!(
6609                err,
6610                crate::dep::DepError::DuplicateNome { ref nome, list }
6611                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
6612            ),
6613            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
6614        );
6615    }
6616
6617    #[test]
6618    fn validate_deps_empty_lists_pass_duplicate_gate() {
6619        // Empty-set identity pin: the bare template (zero deps, zero
6620        // deps_dev) passes the duplicate gate as the gate's identity
6621        // element. A future tighten that conflates "empty" with
6622        // "missing" would regress this baseline.
6623        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6624        c.validate_deps().unwrap();
6625    }
6626
6627    #[test]
6628    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
6629        // Diagnostic-shape pin: the `list:` field tags which list the
6630        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
6631        // `feira lint` run can route the author to the right block in
6632        // their caixa.lisp without re-deriving the list from context.
6633        // Same self-locating shape every peer per-axis diagnostic
6634        // already exposes.
6635        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6636        c.deps_dev = vec![
6637            Dep::simple("dev-thing", "*"),
6638            Dep::simple("dev-thing", "^0.1"),
6639        ];
6640        let err = c.validate_deps().unwrap_err();
6641        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
6642            panic!("expected DuplicateNome from :deps-dev walk");
6643        };
6644        assert_eq!(nome, "dev-thing");
6645        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
6646    }
6647
6648    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
6649
6650    #[test]
6651    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
6652        // Thread-through pin on `:deps`: the per-entry
6653        // `Dep::validate_caracteristicas` gate fires inside
6654        // `Caixa::validate_deps`'s linear walk, so a malformed feature
6655        // list on any `:deps` entry surfaces as a `DepError` from
6656        // `validate_deps` — the same reachability shape every per-entry
6657        // `Dep::validate` arm threads through. Without this pin a future
6658        // shortcut that skips the per-entry `Dep::validate` call on the
6659        // cross-entry-uniqueness path would mask the within-entry
6660        // `:caracteristicas` gates.
6661        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6662        c.deps = vec![Dep {
6663            nome: "caixa-teia".into(),
6664            versao: "^0.1".into(),
6665            fonte: None,
6666            opcional: false,
6667            caracteristicas: vec!["http".into(), "http".into()],
6668        }];
6669        let err = c.validate_deps().unwrap_err();
6670        let crate::dep::DepError::CaracteristicaDuplicate {
6671            nome,
6672            caracteristica,
6673        } = err
6674        else {
6675            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
6676        };
6677        assert_eq!(nome, "caixa-teia");
6678        assert_eq!(caracteristica, "http");
6679    }
6680
6681    #[test]
6682    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
6683        // Peer thread-through pin on `:deps-dev`: same reachability as
6684        // the `:deps` arm above, on the dev-only authoring axis. Pins
6685        // that the `validate_deps` walk visits both lists' per-entry
6686        // gates uniformly. The empty-feature arm carries here so both
6687        // new `:caracteristicas` arms are surfaced via at least one
6688        // `validate_deps` thread-through.
6689        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6690        c.deps_dev = vec![Dep {
6691            nome: "caixa-teia".into(),
6692            versao: "^0.1".into(),
6693            fonte: None,
6694            opcional: false,
6695            caracteristicas: vec![String::new()],
6696        }];
6697        let err = c.validate_deps().unwrap_err();
6698        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
6699            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
6700        };
6701        assert_eq!(nome, "caixa-teia");
6702    }
6703
6704    #[test]
6705    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
6706        // Thread-through pin on `:deps`: the per-entry
6707        // `Dep::validate_caracteristicas` value-shape gate (lifted via
6708        // `crate::render::is_cargo_feature_name`) fires inside
6709        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
6710        // a structurally invalid feature name on any `:deps` entry
6711        // surfaces as `DepError::CaracteristicaInvalid` from
6712        // `validate_deps` — the same reachability shape every per-entry
6713        // `Dep::validate` arm threads through. Without this pin a
6714        // future shortcut that skips the per-entry `Dep::validate` call
6715        // on the cross-entry-uniqueness path would mask the within-
6716        // entry `:caracteristicas` value-shape gate.
6717        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6718        c.deps = vec![Dep {
6719            nome: "caixa-teia".into(),
6720            versao: "^0.1".into(),
6721            fonte: None,
6722            opcional: false,
6723            caracteristicas: vec!["+http".into()],
6724        }];
6725        let err = c.validate_deps().unwrap_err();
6726        let crate::dep::DepError::CaracteristicaInvalid {
6727            nome,
6728            caracteristica,
6729            ..
6730        } = err
6731        else {
6732            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
6733        };
6734        assert_eq!(nome, "caixa-teia");
6735        assert_eq!(caracteristica, "+http");
6736    }
6737
6738    #[test]
6739    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
6740        // Peer thread-through pin on `:deps-dev`: same reachability as
6741        // the `:deps` arm above, on the dev-only authoring axis. The
6742        // `http/json` shape carries here so the segment-separator
6743        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
6744        // confusion footgun) is surfaced via the cross-entry walk too —
6745        // pinning that the `:deps-dev` list visits the same per-entry
6746        // value-shape gate as the `:deps` list.
6747        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6748        c.deps_dev = vec![Dep {
6749            nome: "caixa-teia".into(),
6750            versao: "^0.1".into(),
6751            fonte: None,
6752            opcional: false,
6753            caracteristicas: vec!["http/json".into()],
6754        }];
6755        let err = c.validate_deps().unwrap_err();
6756        let crate::dep::DepError::CaracteristicaInvalid {
6757            nome,
6758            caracteristica,
6759            ..
6760        } = err
6761        else {
6762            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
6763        };
6764        assert_eq!(nome, "caixa-teia");
6765        assert_eq!(caracteristica, "http/json");
6766    }
6767
6768    #[test]
6769    fn to_lisp_preserves_deps() {
6770        let src = r#"
6771(defcaixa
6772  :nome "x"
6773  :versao "0.1.0"
6774  :kind Biblioteca
6775  :deps ((:nome "a" :versao "^0.1")
6776         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
6777"#;
6778        let c1 = Caixa::from_lisp(src).unwrap();
6779        let emitted = c1.to_lisp();
6780        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
6781        assert_eq!(c1.deps, c2.deps);
6782    }
6783
6784    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
6785
6786    fn caixa_with_nome(nome: &str) -> Caixa {
6787        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
6788        c.nome = nome.to_string();
6789        c
6790    }
6791
6792    #[test]
6793    fn validate_nome_accepts_canonical_template() {
6794        // Positive control: the bare `feira init`-style template's
6795        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
6796        // not regress this baseline shape. A future tightening of the
6797        // accepted set surfaces here as a test failure first.
6798        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6799        c.validate_nome().unwrap();
6800    }
6801
6802    #[test]
6803    fn validate_nome_accepts_canonical_forms() {
6804        // Positive-set sweep: each realistic caixa-name shape the K8s
6805        // apiserver accepts as a `metadata.name` label must pass —
6806        // single-word, hyphen-joined, version-suffixed, single-char,
6807        // two-char, digit-start (DNS-1123 allows this; the stricter
6808        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
6809        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
6810        // the peer member-name axis.
6811        for nome in [
6812            "checkout",
6813            "cart-v2",
6814            "a",
6815            "db",
6816            "3rd-party-shim",
6817            "payment-retry",
6818            "0",
6819        ] {
6820            caixa_with_nome(nome)
6821                .validate_nome()
6822                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
6823        }
6824    }
6825
6826    #[test]
6827    fn validate_nome_rejects_empty() {
6828        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
6829        // an empty `:nome` (the derive macro stores the raw String);
6830        // the gate's empty arm names the offending axis with a narrower
6831        // diagnostic than the `NomeInvalid` parse arm would emit.
6832        let c = caixa_with_nome("");
6833        let err = c.validate_nome().unwrap_err();
6834        assert_eq!(err, ManifestError::NomeEmpty);
6835    }
6836
6837    #[test]
6838    fn validate_nome_rejects_uppercase() {
6839        // The canonical "I copied the TitleCase display name verbatim"
6840        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
6841        // admission on every derived artifact (Helm chart, ComputeUnit,
6842        // CNP, HTTPRoute, label values); the gate moves the diagnostic
6843        // to the source `caixa.lisp` and the reason suggests the
6844        // lowercased fix verbatim.
6845        let c = caixa_with_nome("MyApp");
6846        let err = c.validate_nome().unwrap_err();
6847        let ManifestError::NomeInvalid { nome, reason } = err else {
6848            panic!("expected NomeInvalid for uppercase :nome");
6849        };
6850        assert_eq!(nome, "MyApp");
6851        assert!(
6852            reason.contains("uppercase") && reason.contains("myapp"),
6853            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
6854        );
6855    }
6856
6857    #[test]
6858    fn validate_nome_rejects_underscore() {
6859        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
6860        // `_`; the apiserver rejects on admission across every derived
6861        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
6862        // and `:children :caixa` (31bfa43).
6863        let c = caixa_with_nome("my_app");
6864        let err = c.validate_nome().unwrap_err();
6865        assert!(
6866            matches!(
6867                err,
6868                ManifestError::NomeInvalid { ref nome, ref reason }
6869                    if nome == "my_app" && reason.contains('_')
6870            ),
6871            "got {err:?}"
6872        );
6873    }
6874
6875    #[test]
6876    fn validate_nome_rejects_dot() {
6877        // A `:nome` is a single DNS-1123 label, not a subdomain. The
6878        // "I want to namespace with `.`" footgun the gate redirects to
6879        // `-` via the shared predicate's reason wording.
6880        let c = caixa_with_nome("team.app");
6881        let err = c.validate_nome().unwrap_err();
6882        assert!(
6883            matches!(
6884                err,
6885                ManifestError::NomeInvalid { ref nome, ref reason }
6886                    if nome == "team.app" && reason.contains('.')
6887            ),
6888            "got {err:?}"
6889        );
6890    }
6891
6892    #[test]
6893    fn validate_nome_rejects_leading_hyphen() {
6894        // DNS-1123 boundary rule: the label must start with an ASCII
6895        // alphanumeric. Pin the leading-`-` arm explicitly.
6896        let c = caixa_with_nome("-app");
6897        let err = c.validate_nome().unwrap_err();
6898        assert!(
6899            matches!(
6900                err,
6901                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
6902            ),
6903            "got {err:?}"
6904        );
6905    }
6906
6907    #[test]
6908    fn validate_nome_rejects_trailing_hyphen() {
6909        // Symmetric arm of the boundary rule, pinned separately so a
6910        // future relaxation that only checks the leading position
6911        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
6912        // and `_with_trailing_hyphen` on the supervisor / aplicacao
6913        // axes.
6914        let c = caixa_with_nome("app-");
6915        let err = c.validate_nome().unwrap_err();
6916        assert!(
6917            matches!(
6918                err,
6919                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
6920            ),
6921            "got {err:?}"
6922        );
6923    }
6924
6925    #[test]
6926    fn validate_nome_rejects_unicode() {
6927        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
6928        // bytes are rejected by the K8s apiserver on every name axis.
6929        let c = caixa_with_nome("café");
6930        let err = c.validate_nome().unwrap_err();
6931        assert!(
6932            matches!(
6933                err,
6934                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
6935            ),
6936            "got {err:?}"
6937        );
6938    }
6939
6940    #[test]
6941    fn validate_nome_rejects_whitespace() {
6942        // The paste-from-sketch / paste-from-spec footgun. Internal
6943        // whitespace is rejected by every K8s name axis.
6944        let c = caixa_with_nome("my app");
6945        let err = c.validate_nome().unwrap_err();
6946        assert!(
6947            matches!(
6948                err,
6949                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
6950            ),
6951            "got {err:?}"
6952        );
6953    }
6954
6955    #[test]
6956    fn validate_nome_rejects_too_long() {
6957        // 64-byte boundary pin: the K8s apiserver rejects any
6958        // `metadata.name` over 63 bytes at admission; the diagnostic
6959        // names both the 63-byte cap and the actual length so the
6960        // author can shorten in one edit. Mirrors `_too_long` on the
6961        // peer member-/cluster-/child-name axes.
6962        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
6963        let c = caixa_with_nome(&over);
6964        let err = c.validate_nome().unwrap_err();
6965        let ManifestError::NomeInvalid { nome, reason } = err else {
6966            panic!("expected NomeInvalid for over-cap :nome");
6967        };
6968        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
6969        assert!(
6970            reason.contains("63") && reason.contains("64"),
6971            "diagnostic must name the cap + actual length, got {reason:?}"
6972        );
6973    }
6974
6975    #[test]
6976    fn nome_max_length_validates() {
6977        // The 63-byte cap exactly — the boundary-accepting case pinned
6978        // alongside `validate_nome_rejects_too_long` so a future cap
6979        // shift surfaces both arms simultaneously. Mirrors
6980        // `membro_caixa_max_length_validates`,
6981        // `placement_cluster_max_length_validates`,
6982        // `child_caixa_max_length_validates`.
6983        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
6984        caixa_with_nome(&at_cap).validate_nome().unwrap();
6985    }
6986
6987    #[test]
6988    fn nome_empty_takes_precedence_over_invalid() {
6989        // Order pin: the empty arm fires before the predicate is
6990        // consulted. Empty < invalid in self-locating-ness — the
6991        // narrower `NomeEmpty` diagnostic doesn't carry a useless
6992        // `nome: ""` reference into the parser-shaped reason. Mirrors
6993        // `membro_caixa_empty_takes_precedence_over_invalid` on the
6994        // peer axis (3f9d7a0).
6995        let c = caixa_with_nome("");
6996        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
6997    }
6998
6999    #[test]
7000    fn nome_invalid_diagnostic_carries_offending_nome() {
7001        // Diagnostic-shape pin: the error names the offending `:nome`
7002        // verbatim with a non-empty parser-shaped reason, so a `feira
7003        // lint` run can render the diagnostic without re-parsing.
7004        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
7005        let c = caixa_with_nome("MyApp");
7006        let err = c.validate_nome().unwrap_err();
7007        let ManifestError::NomeInvalid { nome, reason } = err else {
7008            panic!("expected NomeInvalid variant");
7009        };
7010        assert_eq!(nome, "MyApp");
7011        assert!(
7012            !reason.is_empty(),
7013            "NomeInvalid `reason` must carry the predicate's wording verbatim"
7014        );
7015    }
7016
7017    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
7018    //
7019    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
7020    // via DNS-1123; this second-axis gate caps the joint
7021    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
7022    // canonical [`crate::lareira_chart_name`] helper's doc comment
7023    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
7024    // "the M4 admission webhook will pin the joint-length invariant
7025    // when it lands". These tests pin it at the manifest-validate
7026    // layer instead, fail-before-pass-after on the 56-byte boundary.
7027
7028    #[test]
7029    fn validate_nome_chart_name_budget_accepts_canonical_template() {
7030        // Positive control: the bare `feira init`-style template's
7031        // `:nome` ("demo") sits far below the cap; the gate must not
7032        // regress this baseline. Same shape every peer
7033        // value-shape-gate baseline pin uses.
7034        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7035        c.validate_nome_chart_name_budget().unwrap();
7036    }
7037
7038    #[test]
7039    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
7040        // Positive-set sweep across the canonical author surface every
7041        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
7042        // `worker`, the `checkout-aplicacao` example members, the
7043        // `akeyless-attest` caixa-tatara fixture). Every value sits
7044        // far below the 55-byte per-`:nome` budget. Same shape every
7045        // peer per-axis baseline pin uses.
7046        for nome in [
7047            "hello-rio",
7048            "cart",
7049            "checkout",
7050            "worker",
7051            "akeyless-attest",
7052            "demo",
7053            "a",
7054        ] {
7055            caixa_with_nome(nome)
7056                .validate_nome_chart_name_budget()
7057                .unwrap_or_else(|e| {
7058                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
7059                });
7060        }
7061    }
7062
7063    #[test]
7064    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
7065        // Boundary-accepting case at the 55-byte per-`:nome` budget —
7066        // the joint chart name is exactly 63 bytes, the DNS-1123 label
7067        // cap. Pinned alongside the rejecting-arm test so a future cap
7068        // shift surfaces both arms simultaneously. Mirrors
7069        // `nome_max_length_validates` on the peer bare-`:nome` axis.
7070        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
7071        caixa_with_nome(&at_cap)
7072            .validate_nome_chart_name_budget()
7073            .unwrap();
7074    }
7075
7076    #[test]
7077    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
7078        // Fail-before-pass-after pin on the 56-byte boundary: the
7079        // smallest `:nome` length that overflows the joint chart-name
7080        // cap. The inner [`is_dns_1123_label`] gate
7081        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
7082        // this gate it silently passed the manifest-validate cascade
7083        // and surfaced as a `helm lint` / apiserver rejection on the
7084        // rendered chart name far from the source `caixa.lisp`, with
7085        // no field naming the overflow. With this gate the diagnostic
7086        // names the offending `:nome` verbatim alongside the rendered
7087        // chart name and the budget, so the author can shorten in one
7088        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
7089        // bare-`:nome` axis.
7090        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7091        let c = caixa_with_nome(&over);
7092        let err = c.validate_nome_chart_name_budget().unwrap_err();
7093        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7094            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
7095        };
7096        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7097        assert_eq!(nome, over);
7098        assert!(
7099            reason.contains("63") && reason.contains("64") && reason.contains("55"),
7100            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
7101             and the per-`:nome` budget (55), got {reason:?}"
7102        );
7103    }
7104
7105    #[test]
7106    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
7107        // The 63-byte `:nome` boundary — passes the bare-`:nome`
7108        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
7109        // joint chart name that overflows the DNS-1123 label cap
7110        // structurally. The most stringent fail-before-pass-after
7111        // surface: every `:nome` in the 56..=63-byte range passed the
7112        // prior cascade and broke at admission.
7113        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7114        let c = caixa_with_nome(&bare_max);
7115        // The bare-`:nome` gate accepts the 63-byte length.
7116        c.validate_nome().unwrap();
7117        // The new joint-length gate rejects it.
7118        let err = c.validate_nome_chart_name_budget().unwrap_err();
7119        assert!(
7120            matches!(
7121                err,
7122                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
7123                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
7124            ),
7125            "got {err:?}"
7126        );
7127    }
7128
7129    #[test]
7130    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
7131        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
7132        // name appears verbatim in the diagnostic so the author sees
7133        // exactly the string the apiserver / `helm lint` would have
7134        // rejected — no re-derivation required to grep the source.
7135        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
7136        // on the bare-`:nome` axis.
7137        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
7138        let c = caixa_with_nome(&over);
7139        let err = c.validate_nome_chart_name_budget().unwrap_err();
7140        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
7141            panic!("expected NomeChartNameBudgetExceeded variant");
7142        };
7143        assert_eq!(nome, over);
7144        let expected_chart = crate::lareira_chart_name(&over);
7145        assert!(
7146            reason.contains(&expected_chart),
7147            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
7148             got {reason:?}"
7149        );
7150        assert!(
7151            reason.contains("lareira-"),
7152            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
7153        );
7154    }
7155
7156    #[test]
7157    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
7158        // Order pin on the layout cascade: the narrower
7159        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
7160        // joint-length budget. A structurally-malformed `:nome` (here:
7161        // uppercase) surfaces its specific shape error rather than
7162        // the chart-name-budget error, even when the joint length
7163        // would also overflow — the narrower diagnostic is more
7164        // self-locating. Mirrors the cascade-precedence pins peer
7165        // gates already use (e.g. `EntradaParaEmpty` before
7166        // `EntradaParaInvalid`).
7167        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7168        let c = caixa_with_nome(&over);
7169        // The bare-shape gate fires first.
7170        let err = c.validate_nome().unwrap_err();
7171        assert!(
7172            matches!(err, ManifestError::NomeInvalid { .. }),
7173            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
7174        );
7175        // And the layout verify cascade surfaces that diagnostic, not
7176        // the budget arm. Inject a path-exists oracle so the cascade
7177        // gets past the manifest-presence check and into the
7178        // value-shape gates.
7179        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7180        let err = crate::LayoutInvariants::verify(
7181            &layout,
7182            &c,
7183            std::path::Path::new("/tmp/caixa-test-fake-root"),
7184        )
7185        .unwrap_err();
7186        let issue = err.to_string();
7187        assert!(
7188            issue.contains("DNS-1123") || issue.contains("uppercase"),
7189            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
7190             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
7191        );
7192    }
7193
7194    #[test]
7195    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
7196        // Cross-axis envelope pin: the layout cascade wraps both
7197        // bare-`:nome` and joint-length-`:nome` failures through the
7198        // same [`LayoutError::NomeViolation`] envelope, since both
7199        // arms are on the `:nome` axis. The user's diagnostic stays
7200        // self-locating ("which axis"), and a future consumer that
7201        // dispatches on the layout-error variant (e.g. a `feira lint`
7202        // exit-code mapping) sees a single per-axis envelope. The
7203        // wrapped `issue:` carries the full inner diagnostic.
7204        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
7205        let c = caixa_with_nome(&over);
7206        // The bare-shape gate accepts.
7207        c.validate_nome().unwrap();
7208        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
7209        let err = crate::LayoutInvariants::verify(
7210            &layout,
7211            &c,
7212            std::path::Path::new("/tmp/caixa-test-fake-root"),
7213        )
7214        .unwrap_err();
7215        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
7216            panic!("expected LayoutError::NomeViolation, got {err:?}");
7217        };
7218        assert_eq!(caixa, over);
7219        assert!(
7220            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
7221            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
7222        );
7223    }
7224
7225    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
7226
7227    fn caixa_with_versao(versao: &str) -> Caixa {
7228        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7229        c.versao = versao.to_string();
7230        c
7231    }
7232
7233    #[test]
7234    fn validate_versao_accepts_canonical_template() {
7235        // Positive control: the bare `feira init`-style template's
7236        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
7237        // must not regress this baseline shape. A future tightening of
7238        // the accepted set surfaces here as a test failure first.
7239        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7240        c.validate_versao().unwrap();
7241    }
7242
7243    #[test]
7244    fn validate_versao_accepts_canonical_forms() {
7245        // Positive-set sweep: each realistic SemVer-2 shape the
7246        // substrate's downstream consumers accept must pass — bare
7247        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
7248        // build metadata (`+build.42`), the combined form, and the
7249        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
7250        // the peer `:nome` axis (6c992f8).
7251        for versao in [
7252            "0.1.0",
7253            "0.0.0",
7254            "1.0.0",
7255            "0.2.0-rc.1",
7256            "1.0.0-alpha.0",
7257            "1.0.0+build.42",
7258            "1.0.0-rc.1+build.42",
7259            "10.20.30",
7260        ] {
7261            caixa_with_versao(versao)
7262                .validate_versao()
7263                .unwrap_or_else(|e| {
7264                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
7265                });
7266        }
7267    }
7268
7269    #[test]
7270    fn validate_versao_rejects_empty() {
7271        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7272        // an empty `:versao` (the derive macro stores the raw String);
7273        // the gate's empty arm names the offending axis with a narrower
7274        // diagnostic than the `VersaoInvalid` parse arm would emit.
7275        // Mirrors `validate_nome_rejects_empty` (6c992f8).
7276        let c = caixa_with_versao("");
7277        let err = c.validate_versao().unwrap_err();
7278        assert_eq!(err, ManifestError::VersaoEmpty);
7279    }
7280
7281    #[test]
7282    fn validate_versao_rejects_git_tag_shape() {
7283        // The canonical "I copied the git tag verbatim" footgun —
7284        // `feira publish` *emits* `v<versao>` git tags, so a leaked
7285        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
7286        // shift every downstream consumer's version axis. `semver`
7287        // rejects the leading `v` at parse time; the gate moves the
7288        // diagnostic to the source `caixa.lisp`.
7289        let c = caixa_with_versao("v0.1.0");
7290        let err = c.validate_versao().unwrap_err();
7291        let ManifestError::VersaoInvalid { versao, reason } = err else {
7292            panic!("expected VersaoInvalid for git-tag-shape :versao");
7293        };
7294        assert_eq!(versao, "v0.1.0");
7295        assert!(
7296            !reason.is_empty(),
7297            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
7298        );
7299    }
7300
7301    #[test]
7302    fn validate_versao_rejects_missing_patch() {
7303        // The canonical "I shortened it" footgun — SemVer-2 requires
7304        // three parts. Cargo's `version =` field accepts the shortened
7305        // form as a requirement, conflating the two leaks across the
7306        // typed `:deps :versao` vs top-level `:versao` axes; the gate
7307        // pins the top-level axis to the strict three-part shape.
7308        let c = caixa_with_versao("0.1");
7309        let err = c.validate_versao().unwrap_err();
7310        assert!(
7311            matches!(
7312                err,
7313                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
7314            ),
7315            "got {err:?}"
7316        );
7317    }
7318
7319    #[test]
7320    fn validate_versao_rejects_requirement_shape() {
7321        // The canonical "I leaked a requirement into a version" footgun —
7322        // the typed `:deps :versao` / `:membros :versao` axes accept
7323        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
7324        // concrete `Version`. Without this gate the two typed surfaces
7325        // would silently overlap, and a top-level `^0.1` would surface
7326        // at `helm install` time as a Chart.yaml version rejection far
7327        // from the source `caixa.lisp`.
7328        let c = caixa_with_versao("^0.1");
7329        let err = c.validate_versao().unwrap_err();
7330        assert!(
7331            matches!(
7332                err,
7333                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
7334            ),
7335            "got {err:?}"
7336        );
7337    }
7338
7339    #[test]
7340    fn validate_versao_rejects_docker_tag_shape() {
7341        // The "I confused it with a docker tag" footgun — `latest`,
7342        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
7343        // SemVer rejects at parse time; the gate moves the diagnostic
7344        // to the source `caixa.lisp`.
7345        for bad in ["latest", "main", "stable"] {
7346            let c = caixa_with_versao(bad);
7347            let err = c.validate_versao().unwrap_err();
7348            assert!(
7349                matches!(
7350                    err,
7351                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
7352                ),
7353                "got {err:?} for {bad:?}"
7354            );
7355        }
7356    }
7357
7358    #[test]
7359    fn validate_versao_rejects_four_part_form() {
7360        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
7361        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
7362        // semver crate rejects the extra `.0` at parse time.
7363        let c = caixa_with_versao("0.1.0.0");
7364        let err = c.validate_versao().unwrap_err();
7365        assert!(
7366            matches!(
7367                err,
7368                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
7369            ),
7370            "got {err:?}"
7371        );
7372    }
7373
7374    #[test]
7375    fn versao_empty_takes_precedence_over_invalid() {
7376        // Order pin: the empty arm fires before the parser is consulted.
7377        // Empty < invalid in self-locating-ness — the narrower
7378        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
7379        // reference into the parser-shaped reason. Mirrors
7380        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
7381        // peer axis.
7382        let c = caixa_with_versao("");
7383        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
7384    }
7385
7386    #[test]
7387    fn versao_invalid_diagnostic_carries_offending_versao() {
7388        // Diagnostic-shape pin: the error names the offending `:versao`
7389        // verbatim with a non-empty parser-shaped reason, so a `feira
7390        // lint` run can render the diagnostic without re-parsing.
7391        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
7392        let c = caixa_with_versao("v0.1.0");
7393        let err = c.validate_versao().unwrap_err();
7394        let ManifestError::VersaoInvalid { versao, reason } = err else {
7395            panic!("expected VersaoInvalid variant");
7396        };
7397        assert_eq!(versao, "v0.1.0");
7398        assert!(
7399            !reason.is_empty(),
7400            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7401        );
7402    }
7403
7404    #[test]
7405    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
7406        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
7407        // for `:upgrade-from :from` must also pass `validate_versao` —
7408        // the two `:versao`-typed surfaces (top-level `:versao`,
7409        // `:upgrade-from :from`) consume the *same* `semver::Version`
7410        // parser, so they must agree on the accepted set. Without this
7411        // pin, a future tightening of one axis could silently diverge
7412        // from the other. Mirrors the `:versao` requirement-axis
7413        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
7414        // commits established.
7415        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
7416            // From the canonical UpgradeFromEntry round-trip fixture
7417            // (`upgrade::tests::round_trip_load_module` peers).
7418            let entry = crate::UpgradeFromEntry {
7419                from: versao.to_string(),
7420                instructions: Vec::new(),
7421            };
7422            entry
7423                .validate()
7424                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
7425            caixa_with_versao(versao)
7426                .validate_versao()
7427                .unwrap_or_else(|e| {
7428                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
7429                });
7430        }
7431    }
7432
7433    // ── Caixa::validate_restart_window — supervisor restart-window
7434    //    folds through the shared `supervisor::duration_codec` ────────
7435
7436    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
7437        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7438        c.kind = CaixaKind::Supervisor;
7439        c.restart_window = window.map(str::to_string);
7440        c
7441    }
7442
7443    #[test]
7444    fn validate_restart_window_accepts_none() {
7445        // The canonical "omit the slot to express no reset" shape — a
7446        // `None` raw string is the absence of the typed
7447        // `:restart-window` slot, which is exactly the SupervisorSpec
7448        // "never reset" semantics. The gate must be a no-op here; a
7449        // future tightening that rejected `None` would force every
7450        // supervisor caixa to authoring-time pin a window even when
7451        // the OTP semantics call for none.
7452        caixa_with_restart_window(None)
7453            .validate_restart_window()
7454            .unwrap();
7455    }
7456
7457    #[test]
7458    fn validate_restart_window_accepts_canonical_forms() {
7459        // Positive-set sweep across the canonical authoring units the
7460        // shared `supervisor::duration_codec::parse` accepts —
7461        // matches the codec-side `parse_accepts_integer_canonical_units`
7462        // pin in supervisor::tests so a future codec-side tightening
7463        // surfaces simultaneously on both axes.
7464        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
7465            caixa_with_restart_window(Some(window))
7466                .validate_restart_window()
7467                .unwrap_or_else(|e| {
7468                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
7469                });
7470        }
7471    }
7472
7473    #[test]
7474    fn validate_restart_window_rejects_fractional_seconds() {
7475        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
7476        // as f64 to 1.5 → renders back as `"1500ms"` on first
7477        // serialize). Prior to the fold + this gate, the inline
7478        // `parse_window_inline` accepted f64 magnitudes and silently
7479        // produced a `Duration::from_secs_f64(1.5)`, divergent from
7480        // the shared codec's integer-magnitude discipline on the
7481        // serde-routed siblings. The gate now surfaces a self-locating
7482        // diagnostic at the manifest layer.
7483        let err = caixa_with_restart_window(Some("1.5s"))
7484            .validate_restart_window()
7485            .unwrap_err();
7486        let ManifestError::RestartWindowMalformed {
7487            restart_window,
7488            reason,
7489        } = err
7490        else {
7491            panic!("expected RestartWindowMalformed for fractional seconds");
7492        };
7493        assert_eq!(restart_window, "1.5s");
7494        assert!(
7495            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
7496            "diagnostic must carry shared-codec wording, got {reason:?}"
7497        );
7498    }
7499
7500    #[test]
7501    fn validate_restart_window_rejects_decimal_shaped_integer() {
7502        // The `"1.0s"` class — numerically `1s` exactly, but the
7503        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
7504        // gets the same canonical-form diagnostic.
7505        let err = caixa_with_restart_window(Some("1.0s"))
7506            .validate_restart_window()
7507            .unwrap_err();
7508        assert!(
7509            matches!(
7510                err,
7511                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7512                    if restart_window == "1.0s"
7513            ),
7514            "got {err:?}"
7515        );
7516    }
7517
7518    #[test]
7519    fn validate_restart_window_rejects_half_unit_minute() {
7520        // `"0.5m"` is the unit-fraction footgun — author writes a
7521        // human-readable half-minute, the prior inline parser silently
7522        // produced `Duration::from_secs_f64(30.0)` and serde
7523        // re-emitted as `"30s"`, rewriting author intent. The gate
7524        // closes the loop at the manifest layer.
7525        let err = caixa_with_restart_window(Some("0.5m"))
7526            .validate_restart_window()
7527            .unwrap_err();
7528        let ManifestError::RestartWindowMalformed {
7529            restart_window,
7530            reason,
7531        } = err
7532        else {
7533            panic!("expected RestartWindowMalformed");
7534        };
7535        assert_eq!(restart_window, "0.5m");
7536        assert!(
7537            reason.contains("\"30s\""),
7538            "diagnostic must point at the canonical-form remediation, got {reason:?}"
7539        );
7540    }
7541
7542    #[test]
7543    fn validate_restart_window_rejects_leading_sign() {
7544        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
7545        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
7546        // and was caught by the `num < 0.0` arm which silently
7547        // returned `None`, dropping the author-supplied window). The
7548        // shared codec's digit-only gate rejects both with a unified
7549        // canonical-form diagnostic; the manifest-layer wrapper names
7550        // the offending value.
7551        for bad in ["+30s", "-30s"] {
7552            let err = caixa_with_restart_window(Some(bad))
7553                .validate_restart_window()
7554                .unwrap_err();
7555            assert!(
7556                matches!(
7557                    err,
7558                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
7559                        if restart_window == bad
7560                ),
7561                "got {err:?} for {bad:?}"
7562            );
7563        }
7564    }
7565
7566    #[test]
7567    fn validate_restart_window_rejects_unknown_unit() {
7568        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
7569        // unit dispatch surfaces an `unknown duration unit` reason;
7570        // the manifest-layer wrapper names the offending value.
7571        let err = caixa_with_restart_window(Some("30x"))
7572            .validate_restart_window()
7573            .unwrap_err();
7574        let ManifestError::RestartWindowMalformed {
7575            restart_window,
7576            reason,
7577        } = err
7578        else {
7579            panic!("expected RestartWindowMalformed for unknown unit");
7580        };
7581        assert_eq!(restart_window, "30x");
7582        assert!(
7583            reason.contains("unknown duration unit"),
7584            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
7585        );
7586    }
7587
7588    #[test]
7589    fn validate_restart_window_rejects_garbage() {
7590        // Pure non-numeric magnitude (`"abc"`) falls through to the
7591        // shared codec's narrower `"bad duration magnitude"` arm. Same
7592        // diagnostic shape as the codec-side
7593        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
7594        let err = caixa_with_restart_window(Some("abc"))
7595            .validate_restart_window()
7596            .unwrap_err();
7597        let ManifestError::RestartWindowMalformed {
7598            restart_window,
7599            reason,
7600        } = err
7601        else {
7602            panic!("expected RestartWindowMalformed for garbage");
7603        };
7604        assert_eq!(restart_window, "abc");
7605        assert!(
7606            reason.contains("bad duration magnitude"),
7607            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
7608        );
7609    }
7610
7611    #[test]
7612    fn validate_restart_window_rejects_empty_string() {
7613        // The empty-after-trim edge case — distinct from the `None`
7614        // canonical "omit the slot" shape. The shared codec's
7615        // digit-only gate refuses an empty magnitude; the manifest
7616        // layer names the offending `""` so the author can grep for
7617        // the literal empty value in their `caixa.lisp` and either
7618        // remove the slot (the canonical "no reset" shape) or pin a
7619        // positive duration.
7620        let err = caixa_with_restart_window(Some(""))
7621            .validate_restart_window()
7622            .unwrap_err();
7623        assert!(
7624            matches!(
7625                err,
7626                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7627                    if restart_window.is_empty()
7628            ),
7629            "got {err:?}"
7630        );
7631    }
7632
7633    #[test]
7634    fn validate_restart_window_diagnostic_carries_offending_value() {
7635        // Diagnostic-shape pin (peer with
7636        // `nome_invalid_diagnostic_carries_offending_nome` /
7637        // `versao_invalid_diagnostic_carries_offending_versao`): the
7638        // error names the offending raw `:restart-window` verbatim
7639        // with a non-empty shared-codec-shaped reason, so a `feira
7640        // lint` run can render the diagnostic without re-parsing.
7641        let err = caixa_with_restart_window(Some("1.5s"))
7642            .validate_restart_window()
7643            .unwrap_err();
7644        let ManifestError::RestartWindowMalformed {
7645            restart_window,
7646            reason,
7647        } = err
7648        else {
7649            panic!("expected RestartWindowMalformed variant");
7650        };
7651        assert_eq!(restart_window, "1.5s");
7652        assert!(
7653            !reason.is_empty(),
7654            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
7655        );
7656    }
7657
7658    #[test]
7659    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
7660        // Behavioral parity pin after the fold (`parse_window_inline`
7661        // deletion): the canonical `"60s"` still produces
7662        // `Duration::from_secs(60)` on the typed view — the fold is
7663        // semantically equivalent to the prior inline parser on the
7664        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
7665        // pin, narrowed to the parser-side contract.
7666        let c = caixa_with_restart_window(Some("60s"));
7667        let view = c.supervisor_view().expect("Supervisor kind has a view");
7668        assert_eq!(
7669            view.restart_window,
7670            Some(std::time::Duration::from_secs(60))
7671        );
7672    }
7673
7674    #[test]
7675    fn supervisor_view_soft_swallows_what_validate_rejects() {
7676        // Parity pin between the view-construction path and the
7677        // manifest-level validator: the same `"1.5s"` that surfaces
7678        // `RestartWindowMalformed` at `validate_restart_window` time
7679        // becomes `restart_window: None` on the typed view (the fold
7680        // preserves the existing best-effort shape of `supervisor_view`).
7681        // The contract is: a layout-verifier / `feira lint` flow that
7682        // cares about the malformed-window axis MUST consult
7683        // `validate_restart_window` — relying solely on the view's
7684        // `None` swallows the diagnostic silently. This pin makes the
7685        // expectation a typed invariant.
7686        let c = caixa_with_restart_window(Some("1.5s"));
7687        let view = c.supervisor_view().expect("Supervisor kind has a view");
7688        assert_eq!(
7689            view.restart_window, None,
7690            "view-construction path soft-swallows the parse error to None"
7691        );
7692        // And the manifest-level validator does NOT soft-swallow:
7693        assert!(
7694            matches!(
7695                c.validate_restart_window().unwrap_err(),
7696                ManifestError::RestartWindowMalformed { ref restart_window, .. }
7697                    if restart_window == "1.5s"
7698            ),
7699            "validator must surface the offending value",
7700        );
7701    }
7702
7703    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
7704
7705    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
7706        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7707        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
7708        c.exe = exe.into_iter().map(String::from).collect();
7709        c.servicos = servicos.into_iter().map(String::from).collect();
7710        c
7711    }
7712
7713    #[test]
7714    fn validate_code_paths_accepts_canonical_template() {
7715        // The bare `Caixa::template` shape is the gate's identity element
7716        // on the canonical authoring shape — `:bibliotecas
7717        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
7718        // that the gate is non-disruptive against every existing caixa.
7719        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7720        c.validate_code_paths().unwrap();
7721    }
7722
7723    #[test]
7724    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
7725        // Positive control sweep: a canonical-shaped path on every slot
7726        // passes. Mirrors the peer
7727        // `behavior::validate_every_slot_relative_is_ok` pin.
7728        let c = caixa_with_code_paths(
7729            vec!["lib/demo.lisp", "lib/helpers.lisp"],
7730            vec!["exe/demo", "exe/tool"],
7731            vec!["servicos/demo.computeunit.yaml"],
7732        );
7733        c.validate_code_paths().unwrap();
7734    }
7735
7736    #[test]
7737    fn validate_code_paths_accepts_all_empty_lists() {
7738        // The empty-list identity element: every Caixa with no declared
7739        // code paths trivially passes (Supervisor / Aplicacao kinds rely
7740        // on this — the OwnCode gate already rejected them before the
7741        // path-shape gate runs in the layout, but the validator itself
7742        // must accept the empty shape).
7743        let c = caixa_with_code_paths(vec![], vec![], vec![]);
7744        c.validate_code_paths().unwrap();
7745    }
7746
7747    #[test]
7748    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
7749        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
7750        let err = c.validate_code_paths().unwrap_err();
7751        assert!(
7752            matches!(
7753                err,
7754                ManifestError::CodePathEmpty {
7755                    slot: ":bibliotecas"
7756                }
7757            ),
7758            "got {err:?}",
7759        );
7760    }
7761
7762    #[test]
7763    fn validate_code_paths_rejects_empty_exe_entry() {
7764        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
7765        let err = c.validate_code_paths().unwrap_err();
7766        assert!(
7767            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
7768            "got {err:?}",
7769        );
7770    }
7771
7772    #[test]
7773    fn validate_code_paths_rejects_empty_servicos_entry() {
7774        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
7775        let err = c.validate_code_paths().unwrap_err();
7776        assert!(
7777            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
7778            "got {err:?}",
7779        );
7780    }
7781
7782    #[test]
7783    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
7784        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
7785        // so an absolute path that resolves on disk silently passes the
7786        // layout's existence check — the canonical sandbox-escape on
7787        // the biblioteca axis.
7788        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7789        let err = c.validate_code_paths().unwrap_err();
7790        let ManifestError::CodePathAbsolute { slot, path } = err else {
7791            panic!("expected CodePathAbsolute, got {err:?}");
7792        };
7793        assert_eq!(slot, ":bibliotecas");
7794        assert_eq!(path, PathBuf::from("/etc/passwd"));
7795    }
7796
7797    #[test]
7798    fn validate_code_paths_rejects_absolute_exe_entry() {
7799        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
7800        let err = c.validate_code_paths().unwrap_err();
7801        let ManifestError::CodePathAbsolute { slot, path } = err else {
7802            panic!("expected CodePathAbsolute, got {err:?}");
7803        };
7804        assert_eq!(slot, ":exe");
7805        assert_eq!(path, PathBuf::from("/usr/bin/env"));
7806    }
7807
7808    #[test]
7809    fn validate_code_paths_rejects_absolute_servicos_entry() {
7810        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
7811        let err = c.validate_code_paths().unwrap_err();
7812        let ManifestError::CodePathAbsolute { slot, path } = err else {
7813            panic!("expected CodePathAbsolute, got {err:?}");
7814        };
7815        assert_eq!(slot, ":servicos");
7816        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
7817    }
7818
7819    #[test]
7820    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
7821        // Canonical "I want a lib from a sibling caixa" footgun on the
7822        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
7823        // downstream, so a leading `..` traverses to the parent of the
7824        // caixa root with no diagnostic at layout time if the resolved
7825        // target exists.
7826        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
7827        let err = c.validate_code_paths().unwrap_err();
7828        let ManifestError::CodePathParentEscape { slot, path } = err else {
7829            panic!("expected CodePathParentEscape, got {err:?}");
7830        };
7831        assert_eq!(slot, ":bibliotecas");
7832        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
7833    }
7834
7835    #[test]
7836    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
7837        // Mid-path `..` defeats the layout's component-aware
7838        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
7839        // `starts_with(<root>/exe)` is true, but the canonical resolution
7840        // lives outside the caixa root. Caught regardless of where the
7841        // `..` sits — mirrors the peer
7842        // `behavior::validate_rejects_parent_escape_mid_path` pin.
7843        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
7844        let err = c.validate_code_paths().unwrap_err();
7845        let ManifestError::CodePathParentEscape { slot, path } = err else {
7846            panic!("expected CodePathParentEscape, got {err:?}");
7847        };
7848        assert_eq!(slot, ":exe");
7849        assert_eq!(path, PathBuf::from("exe/../../escape"));
7850    }
7851
7852    #[test]
7853    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
7854        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
7855        let err = c.validate_code_paths().unwrap_err();
7856        let ManifestError::CodePathParentEscape { slot, path } = err else {
7857            panic!("expected CodePathParentEscape, got {err:?}");
7858        };
7859        assert_eq!(slot, ":servicos");
7860        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
7861    }
7862
7863    #[test]
7864    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
7865        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
7866        // `:servicos`. A manifest with malformed entries on all three
7867        // surfaces surfaces the `:bibliotecas` defect first, mirroring
7868        // the canonical declaration order
7869        // `Caixa::declared_foreign_code_slots` already establishes for
7870        // the foreign-code-slot diagnostic.
7871        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
7872        let err = c.validate_code_paths().unwrap_err();
7873        assert!(
7874            matches!(
7875                err,
7876                ManifestError::CodePathEmpty {
7877                    slot: ":bibliotecas"
7878                }
7879            ),
7880            "got {err:?}",
7881        );
7882    }
7883
7884    #[test]
7885    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
7886        // Within-slot precedence pin: empty → absolute → parent-escape,
7887        // matching the [`PathShapeViolation`] arm-ordering every peer
7888        // `is_sandboxed_relative_path` caller follows (b0c8389
7889        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
7890        // `:bibliotecas` list whose first entry is empty *and* whose
7891        // later entries are absolute/parent-escape surfaces the empty
7892        // arm first, on the lexicographically-earliest offending entry.
7893        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
7894        let err = c.validate_code_paths().unwrap_err();
7895        assert!(
7896            matches!(
7897                err,
7898                ManifestError::CodePathEmpty {
7899                    slot: ":bibliotecas"
7900                }
7901            ),
7902            "got {err:?}",
7903        );
7904    }
7905
7906    #[test]
7907    fn validate_code_paths_first_offender_per_slot_wins() {
7908        // Within a single slot, the first declaration-order offender
7909        // surfaces — pins that the gate is left-to-right deterministic
7910        // (peer of every `*_first_collision_*` pin on duplicate gates).
7911        let c = caixa_with_code_paths(
7912            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
7913            vec![],
7914            vec![],
7915        );
7916        let err = c.validate_code_paths().unwrap_err();
7917        let ManifestError::CodePathAbsolute { slot, path } = err else {
7918            panic!("expected CodePathAbsolute, got {err:?}");
7919        };
7920        assert_eq!(slot, ":bibliotecas");
7921        assert_eq!(path, PathBuf::from("/etc/escape"));
7922    }
7923
7924    #[test]
7925    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
7926        // Diagnostic-shape pin (peer with
7927        // `nome_invalid_diagnostic_carries_offending_nome` /
7928        // `versao_invalid_diagnostic_carries_offending_versao`): the
7929        // error's Display surfaces both the offending `:slot` tag and
7930        // the offending path verbatim, so a `feira lint` run can render
7931        // the diagnostic without re-parsing.
7932        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
7933        let rendered = c.validate_code_paths().unwrap_err().to_string();
7934        assert!(
7935            rendered.contains(":bibliotecas"),
7936            "diagnostic must name the offending slot: {rendered}",
7937        );
7938        assert!(
7939            rendered.contains("/etc/passwd"),
7940            "diagnostic must quote the offending path: {rendered}",
7941        );
7942    }
7943
7944    #[test]
7945    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
7946        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
7947        // axis. Without the gate `feira build` re-parses the same lib
7948        // twice, wasting work and silently masking the author's intent
7949        // to declare a *second* biblioteca.
7950        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
7951        let err = c.validate_code_paths().unwrap_err();
7952        let ManifestError::CodePathDuplicate { slot, path } = err else {
7953            panic!("expected CodePathDuplicate, got {err:?}");
7954        };
7955        assert_eq!(slot, ":bibliotecas");
7956        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
7957    }
7958
7959    #[test]
7960    fn validate_code_paths_rejects_duplicate_exe_entry() {
7961        // Same footgun on the Binario surface. The future `caixa-flake`
7962        // emitter that materializes each `:exe` entry as a flake
7963        // `packages.<name>` derivation would collide on the duplicate
7964        // package key — surfaced here at the typed-validate layer with a
7965        // self-locating diagnostic instead.
7966        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
7967        let err = c.validate_code_paths().unwrap_err();
7968        let ManifestError::CodePathDuplicate { slot, path } = err else {
7969            panic!("expected CodePathDuplicate, got {err:?}");
7970        };
7971        assert_eq!(slot, ":exe");
7972        assert_eq!(path, PathBuf::from("exe/cli"));
7973    }
7974
7975    #[test]
7976    fn validate_code_paths_rejects_duplicate_servicos_entry() {
7977        // Same footgun on the Servico surface. The peer caixa-helm /
7978        // caixa-flux renderers refuse `:servicos.len() != 1` with the
7979        // narrower `UnsupportedServicoCount` diagnostic, but that
7980        // diagnostic surfaces "too many servicos" without naming
7981        // "duplicate entry" — the typed self-locating framing only lands
7982        // at this gate.
7983        let c = caixa_with_code_paths(
7984            vec![],
7985            vec![],
7986            vec![
7987                "servicos/demo.computeunit.yaml",
7988                "servicos/demo.computeunit.yaml",
7989            ],
7990        );
7991        let err = c.validate_code_paths().unwrap_err();
7992        let ManifestError::CodePathDuplicate { slot, path } = err else {
7993            panic!("expected CodePathDuplicate, got {err:?}");
7994        };
7995        assert_eq!(slot, ":servicos");
7996        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
7997    }
7998
7999    #[test]
8000    fn validate_code_paths_accepts_same_path_across_slots() {
8001        // Per-list scope pin: a `:bibliotecas` entry that happens to
8002        // collide with an `:exe` or `:servicos` entry as a *string* is
8003        // not a duplicate by this gate (each list gets its own HashSet),
8004        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
8005        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
8006        // shape on the dep axis). The structural `starts_with(<exe |
8007        // servicos>_dir)` fence at layout time prevents the realistic
8008        // cross-slot collision case from existing on disk, but the gate's
8009        // per-list scope is correct independent of that downstream fence.
8010        let c = caixa_with_code_paths(
8011            vec!["lib/x.lisp"],
8012            vec!["exe/x"],
8013            vec!["servicos/x.computeunit.yaml"],
8014        );
8015        c.validate_code_paths().unwrap();
8016    }
8017
8018    #[test]
8019    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
8020        // Within-slot ordering pin: structural defects (empty / absolute
8021        // / parent-escape) fire before the duplicate gate on the same
8022        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
8023        // surfaces the narrower `CodePathEmpty` for the empty entry
8024        // first, not the duplicate on the later pair — same arm-ordering
8025        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
8026        // `:autores` 86c769b, `:deps` 359fba5).
8027        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
8028        let err = c.validate_code_paths().unwrap_err();
8029        assert!(
8030            matches!(
8031                err,
8032                ManifestError::CodePathEmpty {
8033                    slot: ":bibliotecas"
8034                }
8035            ),
8036            "got {err:?}",
8037        );
8038    }
8039
8040    #[test]
8041    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
8042        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
8043        // duplicates surface before `:exe` duplicates, matching the
8044        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
8045        // order every peer per-slot diagnostic on this surface follows.
8046        let c = caixa_with_code_paths(
8047            vec!["lib/x.lisp", "lib/x.lisp"],
8048            vec!["exe/y", "exe/y"],
8049            vec![],
8050        );
8051        let err = c.validate_code_paths().unwrap_err();
8052        let ManifestError::CodePathDuplicate { slot, path } = err else {
8053            panic!("expected CodePathDuplicate, got {err:?}");
8054        };
8055        assert_eq!(slot, ":bibliotecas");
8056        assert_eq!(path, PathBuf::from("lib/x.lisp"));
8057    }
8058
8059    #[test]
8060    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
8061        // Diagnostic-shape pin (peer with
8062        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8063        // on the structural arm): the duplicate-arm Display surfaces both
8064        // the offending `:slot` tag and the offending path verbatim, so a
8065        // `feira lint` run can render the diagnostic without re-parsing.
8066        let c = caixa_with_code_paths(
8067            vec![],
8068            vec![],
8069            vec![
8070                "servicos/demo.computeunit.yaml",
8071                "servicos/demo.computeunit.yaml",
8072            ],
8073        );
8074        let rendered = c.validate_code_paths().unwrap_err().to_string();
8075        assert!(
8076            rendered.contains(":servicos"),
8077            "diagnostic must name the offending slot: {rendered}",
8078        );
8079        assert!(
8080            rendered.contains("servicos/demo.computeunit.yaml"),
8081            "diagnostic must quote the offending path: {rendered}",
8082        );
8083    }
8084
8085    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
8086    //
8087    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
8088    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
8089    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
8090    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
8091    // at parse time — the same downstream consumer the peer `:behavior
8092    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
8093    // `:upgrade-from :state-change :script` (33cc830,
8094    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
8095    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
8096    // nix-built executable surface (`"exe/<name>"` shape per the canonical
8097    // [`crate::LayoutError::ExeOutsideDir`] error message and every
8098    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
8099    // is the `.computeunit.yaml` ComputeUnit-CR axis.
8100
8101    #[test]
8102    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
8103        // Canonical "I dragged the wrong file from the workspace tree"
8104        // footgun on the biblioteca axis. Without the gate `feira build`
8105        // hands the extensionless path to `tatara_lisp::read` and fails
8106        // with a parser-shaped diagnostic far from the source caixa.lisp,
8107        // with no field naming the offending `:bibliotecas` entry.
8108        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
8109            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8110            let err = c.validate_code_paths().unwrap_err();
8111            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8112                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8113            };
8114            assert_eq!(slot, ":bibliotecas");
8115            assert_eq!(path, PathBuf::from(relpath));
8116        }
8117    }
8118
8119    #[test]
8120    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
8121        // Wrong-extension sweep across common authoring footguns. Same
8122        // sweep posture as the peer
8123        // `behavior::validate_rejects_wrong_extension` (c97815a) and
8124        // `upgrade::tests::state_change_rejects_wrong_extension_script`
8125        // (33cc830) cases.
8126        for relpath in [
8127            "lib/demo.rs",
8128            "lib/demo.txt",
8129            "lib/demo.md",
8130            "lib/demo.json",
8131            "lib/demo.yaml",
8132            "lib/demo.toml",
8133            "lib/demo.lisp.bak",
8134            "lib/demo.lispx",
8135            "lib/demo.lis",
8136        ] {
8137            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8138            let err = c.validate_code_paths().unwrap_err();
8139            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8140                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8141            };
8142            assert_eq!(slot, ":bibliotecas");
8143            assert_eq!(path, PathBuf::from(relpath));
8144        }
8145    }
8146
8147    #[test]
8148    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
8149        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
8150        // contract. An uppercase `.LISP` shape that the layout's existence
8151        // check would (case-insensitively, on case-insensitive volumes)
8152        // match the on-disk file still mismatches the canonical form the
8153        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
8154        // contract. Mirrors the peer
8155        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
8156        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
8157        // (33cc830) sweeps.
8158        for relpath in [
8159            "lib/demo.LISP",
8160            "lib/demo.Lisp",
8161            "lib/demo.LiSp",
8162            "lib/demo.lISP",
8163        ] {
8164            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8165            let err = c.validate_code_paths().unwrap_err();
8166            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8167                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
8168            };
8169            assert_eq!(slot, ":bibliotecas");
8170            assert_eq!(path, PathBuf::from(relpath));
8171        }
8172    }
8173
8174    #[test]
8175    fn validate_code_paths_accepts_canonical_lisp_shapes() {
8176        // Positive-control sweep through every canonical authoring shape
8177        // every in-tree fixture and the `Caixa::template` scaffold use.
8178        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
8179        // (c97815a) and the lifted predicate's own
8180        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
8181        // (33cc830).
8182        for relpath in [
8183            "lib/demo.lisp",
8184            "lib/handlers.lisp",
8185            "lib/migrations/v01-to-v02.lisp",
8186            "demo.lisp",
8187            "a.lisp",
8188            "./lib/demo.lisp",
8189            "lib/./handlers.lisp",
8190            "lib/migrations/v.0.1.lisp",
8191        ] {
8192            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
8193            c.validate_code_paths()
8194                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8195        }
8196    }
8197
8198    #[test]
8199    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
8200        // The file-type gate is per-slot — only `:bibliotecas` carries the
8201        // tatara-lisp-source contract. An extensionless `:exe` entry
8202        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
8203        // canonical shapes every in-tree fixture uses, and must continue
8204        // to pass validate. Pins that a future tightening that broadens
8205        // the `.lisp` gate to either axis surfaces as a test failure
8206        // rather than as a silent breaking change to existing valid
8207        // manifests.
8208        let c = caixa_with_code_paths(
8209            vec![],
8210            vec!["exe/demo", "exe/tool"],
8211            vec!["servicos/demo.computeunit.yaml"],
8212        );
8213        c.validate_code_paths().unwrap();
8214    }
8215
8216    #[test]
8217    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
8218        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
8219        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
8220        // sandbox-shape diagnostic first (the `.lisp` remediation would
8221        // be misleading when the offending path can never resolve under
8222        // the caixa root anyway). Mirrors the peer
8223        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
8224        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
8225        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
8226        // on `:upgrade-from :state-change :script` (33cc830).
8227        //
8228        // Empty wins (the strictly-smaller-scope structural arm).
8229        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8230        assert!(
8231            matches!(
8232                c.validate_code_paths().unwrap_err(),
8233                ManifestError::CodePathEmpty {
8234                    slot: ":bibliotecas"
8235                }
8236            ),
8237            "empty must win over non-lisp-extension",
8238        );
8239        // Absolute wins (the path can't resolve under the caixa root).
8240        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8241        let err = c.validate_code_paths().unwrap_err();
8242        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8243            panic!("absolute must win over non-lisp-extension, got {err:?}");
8244        };
8245        assert_eq!(slot, ":bibliotecas");
8246        // ParentEscape wins (the path escapes the caixa root).
8247        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
8248        let err = c.validate_code_paths().unwrap_err();
8249        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8250            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
8251        };
8252        assert_eq!(slot, ":bibliotecas");
8253    }
8254
8255    #[test]
8256    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
8257        // Within-slot precedence pin: the per-entry file-type shape gate
8258        // fires before the cross-entry duplicate gate, so the narrower
8259        // structural defect dominates the uniqueness diagnostic. A
8260        // `("lib/x.txt" "lib/x.txt")` shape surfaces
8261        // `CodePathNonLispExtension` on the first entry rather than
8262        // `CodePathDuplicate` on the pair — same posture every per-entry
8263        // shape-gate-precedes-duplicate cascade follows on this surface
8264        // (the empty / absolute / parent-escape arms already precede the
8265        // duplicate arm; the lifted file-type arm joins that set).
8266        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
8267        let err = c.validate_code_paths().unwrap_err();
8268        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
8269            panic!("expected CodePathNonLispExtension, got {err:?}");
8270        };
8271        assert_eq!(slot, ":bibliotecas");
8272        assert_eq!(path, PathBuf::from("lib/x.txt"));
8273    }
8274
8275    #[test]
8276    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
8277        // Diagnostic-shape pin (peer with
8278        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8279        // on the sandbox-shape arms and
8280        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
8281        // on the duplicate arm): the file-type-arm Display surfaces both
8282        // the offending `:slot` tag, the offending path verbatim, and the
8283        // expected `.lisp` extension named in the remediation text, so a
8284        // `feira lint` run can render the diagnostic without re-parsing.
8285        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
8286        let rendered = c.validate_code_paths().unwrap_err().to_string();
8287        assert!(
8288            rendered.contains(":bibliotecas"),
8289            "diagnostic must name the offending slot: {rendered}",
8290        );
8291        assert!(
8292            rendered.contains("lib/demo.rs"),
8293            "diagnostic must quote the offending path: {rendered}",
8294        );
8295        assert!(
8296            rendered.contains(".lisp"),
8297            "diagnostic must name the expected extension: {rendered}",
8298        );
8299    }
8300
8301    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
8302    //
8303    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
8304    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
8305    // contract. The peer caixa-helm / caixa-flux renderers consume each
8306    // `:servicos` entry through `serde_yaml::from_str` as a typed
8307    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
8308    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
8309    // axis `Path::extension` can't express on its own.
8310
8311    #[test]
8312    fn validate_code_paths_rejects_no_extension_servicos_entry() {
8313        // Canonical "I dragged the wrong file from the workspace tree"
8314        // footgun on the Servico axis. Without the gate the peer
8315        // caixa-helm / caixa-flux renderers hand the extensionless path
8316        // to `serde_yaml::from_str` and fail with a parser-shaped
8317        // diagnostic far from the source caixa.lisp, with no field
8318        // naming the offending `:servicos` entry.
8319        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
8320            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8321            let err = c.validate_code_paths().unwrap_err();
8322            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8323                panic!(
8324                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8325                     got {err:?}"
8326                );
8327            };
8328            assert_eq!(slot, ":servicos");
8329            assert_eq!(path, PathBuf::from(relpath));
8330        }
8331    }
8332
8333    #[test]
8334    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
8335        // Wrong-extension sweep across common authoring footguns on the
8336        // Servico axis. Bare `.yaml` is the canonical "I forgot the
8337        // `.computeunit` segment" typo; the off-by-one-segment shapes
8338        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
8339        // bare `Path::extension` view but mismatch the typed compound
8340        // suffix the renderers' `serde_yaml::from_str` consumer demands.
8341        // Same sweep-posture as the peer
8342        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
8343        // (64772a9) on the sibling tatara-lisp-source axis.
8344        for relpath in [
8345            "servicos/demo.yaml",
8346            "servicos/demo.yml",
8347            "servicos/demo.json",
8348            "servicos/demo.toml",
8349            "servicos/demo.txt",
8350            "servicos/demo.computeunit.yaml.bak",
8351            "servicos/demo.computeunit.yam",
8352            "servicos/demo.computeunit",
8353            "servicos/demo-computeunit.yaml",
8354            "servicos/demo_computeunit.yaml",
8355        ] {
8356            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8357            let err = c.validate_code_paths().unwrap_err();
8358            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8359                panic!(
8360                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8361                     got {err:?}"
8362                );
8363            };
8364            assert_eq!(slot, ":servicos");
8365            assert_eq!(path, PathBuf::from(relpath));
8366        }
8367    }
8368
8369    #[test]
8370    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
8371        // Case-sensitivity sweep — pins the strict lowercase
8372        // `.computeunit.yaml` contract. A case-folded shape that the
8373        // layout's existence check would (case-insensitively, on
8374        // case-insensitive volumes) match the on-disk file still
8375        // mismatches the canonical form the codec emits, breaking the
8376        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
8377        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
8378        // (64772a9) sweep on the sibling tatara-lisp-source axis.
8379        for relpath in [
8380            "servicos/demo.ComputeUnit.yaml",
8381            "servicos/demo.COMPUTEUNIT.yaml",
8382            "servicos/demo.computeunit.YAML",
8383            "servicos/demo.computeunit.Yaml",
8384            "servicos/demo.COMPUTEUNIT.YAML",
8385        ] {
8386            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8387            let err = c.validate_code_paths().unwrap_err();
8388            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8389                panic!(
8390                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8391                     got {err:?}"
8392                );
8393            };
8394            assert_eq!(slot, ":servicos");
8395            assert_eq!(path, PathBuf::from(relpath));
8396        }
8397    }
8398
8399    #[test]
8400    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
8401        // Degenerate hidden-file shape: a file name exactly equal to the
8402        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
8403        // the structural "Servico declared with no identity" footgun.
8404        // The substrate identifies each ComputeUnit by the file-stem
8405        // segment that precedes `.computeunit.yaml` (the rendered
8406        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
8407        // the M3 `:contratos` membership lookup), so an empty stem
8408        // leaves the Servico unidentifiable. Pinned at the typed-axis
8409        // level so a future regression that drops the `name.len() >
8410        // SUFFIX.len()` bound at the predicate surfaces here, not
8411        // piecemeal as a `lareira-` chart-name collision at render time.
8412        for relpath in ["servicos/.computeunit.yaml"] {
8413            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8414            let err = c.validate_code_paths().unwrap_err();
8415            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8416                panic!(
8417                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
8418                     got {err:?}"
8419                );
8420            };
8421            assert_eq!(slot, ":servicos");
8422            assert_eq!(path, PathBuf::from(relpath));
8423        }
8424    }
8425
8426    #[test]
8427    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
8428        // Positive-control sweep through every canonical authoring shape
8429        // every in-tree fixture and the `Caixa::template` scaffold use.
8430        // Mirrors the peer
8431        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
8432        // and the lifted predicate's own
8433        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
8434        // render.rs.
8435        for relpath in [
8436            "servicos/demo.computeunit.yaml",
8437            "servicos/hello-rio.computeunit.yaml",
8438            "servicos/my-service.computeunit.yaml",
8439            "servicos/a.computeunit.yaml",
8440            "./servicos/demo.computeunit.yaml",
8441            "servicos/./demo.computeunit.yaml",
8442            "servicos/sub/nested.computeunit.yaml",
8443            "servicos/v0.1.computeunit.yaml",
8444        ] {
8445            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
8446            c.validate_code_paths()
8447                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
8448        }
8449    }
8450
8451    #[test]
8452    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
8453        // The file-type gate is per-slot — only `:servicos` carries the
8454        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
8455        // entry and an extensionless `:exe` entry are the canonical
8456        // shapes every in-tree fixture uses, and must continue to pass
8457        // validate. Peer of
8458        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
8459        // (64772a9) — together pin that the typed
8460        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
8461        // cross-axis leakage in either direction.
8462        let c = caixa_with_code_paths(
8463            vec!["lib/demo.lisp"],
8464            vec!["exe/demo", "exe/tool"],
8465            vec!["servicos/demo.computeunit.yaml"],
8466        );
8467        c.validate_code_paths().unwrap();
8468    }
8469
8470    #[test]
8471    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
8472        // Cross-arm precedence pin: a `:servicos` entry that is *both*
8473        // sandbox-escaping and wrong-extension surfaces the more
8474        // fundamental sandbox-shape diagnostic first (the
8475        // `.computeunit.yaml` remediation would be misleading when the
8476        // offending path can never resolve under the caixa root
8477        // anyway). Mirrors the peer
8478        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
8479        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
8480        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
8481        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
8482        // table establishes.
8483        //
8484        // Empty wins (the strictly-smaller-scope structural arm).
8485        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8486        assert!(
8487            matches!(
8488                c.validate_code_paths().unwrap_err(),
8489                ManifestError::CodePathEmpty { slot: ":servicos" }
8490            ),
8491            "empty must win over non-computeunit-yaml-extension",
8492        );
8493        // Absolute wins (the path can't resolve under the caixa root).
8494        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
8495        let err = c.validate_code_paths().unwrap_err();
8496        let ManifestError::CodePathAbsolute { slot, .. } = err else {
8497            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
8498        };
8499        assert_eq!(slot, ":servicos");
8500        // ParentEscape wins (the path escapes the caixa root).
8501        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
8502        let err = c.validate_code_paths().unwrap_err();
8503        let ManifestError::CodePathParentEscape { slot, .. } = err else {
8504            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
8505        };
8506        assert_eq!(slot, ":servicos");
8507    }
8508
8509    #[test]
8510    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
8511        // Within-slot precedence pin: the per-entry file-type shape gate
8512        // fires before the cross-entry duplicate gate, so the narrower
8513        // structural defect dominates the uniqueness diagnostic. A
8514        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
8515        // `CodePathNonComputeUnitYamlExtension` on the first entry
8516        // rather than `CodePathDuplicate` on the pair — same posture
8517        // every per-entry shape-gate-precedes-duplicate cascade follows
8518        // on this surface, peer of the 64772a9 `:bibliotecas`
8519        // `("lib/x.txt" "lib/x.txt")` ordering.
8520        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
8521        let err = c.validate_code_paths().unwrap_err();
8522        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
8523            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
8524        };
8525        assert_eq!(slot, ":servicos");
8526        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
8527    }
8528
8529    #[test]
8530    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
8531     {
8532        // Diagnostic-shape pin (peer with
8533        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
8534        // on the sibling tatara-lisp-source axis): the file-type-arm
8535        // Display surfaces both the offending `:slot` tag, the
8536        // offending path verbatim, and the expected
8537        // `.computeunit.yaml` compound suffix named in the remediation
8538        // text, so a `feira lint` run can render the diagnostic without
8539        // re-parsing.
8540        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
8541        let rendered = c.validate_code_paths().unwrap_err().to_string();
8542        assert!(
8543            rendered.contains(":servicos"),
8544            "diagnostic must name the offending slot: {rendered}",
8545        );
8546        assert!(
8547            rendered.contains("servicos/demo.yaml"),
8548            "diagnostic must quote the offending path: {rendered}",
8549        );
8550        assert!(
8551            rendered.contains(".computeunit.yaml"),
8552            "diagnostic must name the expected compound suffix: {rendered}",
8553        );
8554    }
8555
8556    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
8557
8558    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
8559        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8560        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
8561        c
8562    }
8563
8564    #[test]
8565    fn validate_etiquetas_accepts_empty_list() {
8566        // The empty-list identity: every caixa with no declared tags
8567        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
8568        // so the gate is non-disruptive against every existing manifest.
8569        let c = caixa_with_etiquetas(vec![]);
8570        c.validate_etiquetas().unwrap();
8571    }
8572
8573    #[test]
8574    fn validate_etiquetas_accepts_canonical_forms() {
8575        // Positive control sweep: a canonical-shaped non-empty distinct
8576        // tag list passes, mirroring the example checkout-aplicacao
8577        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
8578        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
8579        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
8580        c.validate_etiquetas().unwrap();
8581    }
8582
8583    #[test]
8584    fn validate_etiquetas_rejects_empty_entry() {
8585        // Canonical paste-from-blank-doc footgun. Without the gate the
8586        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
8587        // no-op tag indexing nothing in the future caixa-registry.
8588        let c = caixa_with_etiquetas(vec![""]);
8589        let err = c.validate_etiquetas().unwrap_err();
8590        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8591    }
8592
8593    #[test]
8594    fn validate_etiquetas_rejects_duplicate_entry() {
8595        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
8596        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
8597        // collect at chart render — a "second wins / one silently
8598        // disappears" shape divergent from every peer typed-graph set
8599        // gate. The duplicate-arm names the offending tag verbatim.
8600        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8601        let err = c.validate_etiquetas().unwrap_err();
8602        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8603            panic!("expected EtiquetaDuplicate, got {err:?}");
8604        };
8605        assert_eq!(etiqueta, "demo");
8606    }
8607
8608    #[test]
8609    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
8610        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
8611        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
8612        // structural "this entry has no value" defect dominates the
8613        // cross-entry uniqueness diagnostic. Mirrors the peer
8614        // empty-before-duplicate cascades on `:caracteristicas`
8615        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8616        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
8617        // `MembroDuplicate`).
8618        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
8619        let err = c.validate_etiquetas().unwrap_err();
8620        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8621    }
8622
8623    #[test]
8624    fn validate_etiquetas_duplicate_reports_first_collision() {
8625        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8626        // duplicate (the lexicographically-earliest offending position
8627        // — the second `"a"` at index 2 collides with the first `"a"`
8628        // at index 0), not the later `"b"` collision at index 3,
8629        // peer with every other first-collision diagnostic posture on
8630        // this surface (`validate_load_singularity_reports_first_collision`,
8631        // `validate_cleanup_singularity_reports_first_collision`).
8632        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
8633        let err = c.validate_etiquetas().unwrap_err();
8634        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
8635            panic!("expected EtiquetaDuplicate, got {err:?}");
8636        };
8637        assert_eq!(etiqueta, "a");
8638    }
8639
8640    #[test]
8641    fn validate_etiquetas_case_sensitive() {
8642        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
8643        // mirroring the peer `:membros :caixa` / `:children :caixa`
8644        // exact-string-match discipline. The shape gate this routine
8645        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
8646        // grammar) accepts mixed case — crates.io's keyword rule is
8647        // "case-insensitive" at the index layer but admits mixed case
8648        // at the entry layer (the canonical Helm chart `keywords:`
8649        // shape is lowercase by convention, but the grammar admits
8650        // uppercase). Case-sensitivity at the duplicate-set layer
8651        // remains structural — two distinct strings are two distinct
8652        // entries.
8653        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
8654        c.validate_etiquetas().unwrap();
8655    }
8656
8657    #[test]
8658    fn validate_etiquetas_diagnostic_carries_offending_tag() {
8659        // Diagnostic-shape pin (peer with
8660        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
8661        // the error's Display surfaces the offending tag verbatim, so a
8662        // `feira lint` run can render the diagnostic without re-parsing
8663        // and the author can grep their caixa.lisp for the offending
8664        // value.
8665        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
8666        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8667        assert!(
8668            rendered.contains(":etiquetas"),
8669            "diagnostic must name the offending slot: {rendered}",
8670        );
8671        assert!(
8672            rendered.contains("demo"),
8673            "diagnostic must quote the offending tag: {rendered}",
8674        );
8675    }
8676
8677    #[test]
8678    fn validate_etiquetas_rejects_leading_whitespace_entry() {
8679        // Canonical paste-from-aligned-doc footgun. Without the shape
8680        // gate `" mesh"` silently passed validate and landed as a
8681        // YAML plain-style scalar with leading whitespace in the
8682        // rendered Chart.yaml `keywords:` array — every YAML 1.2
8683        // dumper trims leading whitespace from plain-style scalars,
8684        // so the authored space round-tripped inconsistently back
8685        // through `caixa.lisp`. Mirrors the peer
8686        // `validate_autores_rejects_leading_whitespace_entry`.
8687        let c = caixa_with_etiquetas(vec![" mesh"]);
8688        let err = c.validate_etiquetas().unwrap_err();
8689        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8690            panic!("expected EtiquetaInvalid, got {err:?}");
8691        };
8692        assert_eq!(etiqueta, " mesh");
8693        assert!(reason.contains("whitespace"), "got: {reason}");
8694    }
8695
8696    #[test]
8697    fn validate_etiquetas_rejects_embedded_newline_entry() {
8698        // Canonical paste-from-multiline-doc footgun — the author
8699        // pasted a multi-tag block into one `:etiquetas` entry
8700        // instead of splitting into one entry per tag. Without the
8701        // shape gate `"mesh\nhttp"` silently passed validate and
8702        // landed as a YAML-illegal multi-line scalar in the rendered
8703        // Chart.yaml `keywords:` array.
8704        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8705        let err = c.validate_etiquetas().unwrap_err();
8706        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8707            panic!("expected EtiquetaInvalid, got {err:?}");
8708        };
8709        assert_eq!(etiqueta, "mesh\nhttp");
8710        assert!(reason.contains("newline"), "got: {reason}");
8711    }
8712
8713    #[test]
8714    fn validate_etiquetas_rejects_embedded_comma_entry() {
8715        // Canonical CSV-list-separator-confusion footgun: the author
8716        // confused the CSV-style separator convention with the
8717        // `:etiquetas` list grammar. Without the shape gate
8718        // `"mesh,http,grpc"` silently passed validate and landed as a
8719        // single malformed search tag in the rendered Chart.yaml
8720        // `keywords:` array — Artifact Hub's keyword index would
8721        // either silently drop the tag or index it as
8722        // `mesh,http,grpc` instead of three separate tags.
8723        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
8724        let err = c.validate_etiquetas().unwrap_err();
8725        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8726            panic!("expected EtiquetaInvalid, got {err:?}");
8727        };
8728        assert_eq!(etiqueta, "mesh,http,grpc");
8729        assert!(reason.contains('`'), "got: {reason}");
8730        assert!(reason.contains(','), "got: {reason}");
8731    }
8732
8733    #[test]
8734    fn validate_etiquetas_rejects_embedded_slash_entry() {
8735        // Canonical path-separator-confusion footgun: the author
8736        // confused namespace-path notation with the keyword grammar.
8737        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
8738        let err = c.validate_etiquetas().unwrap_err();
8739        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8740            panic!("expected EtiquetaInvalid, got {err:?}");
8741        };
8742        assert_eq!(etiqueta, "caixa/servico");
8743        assert!(reason.contains('/'), "got: {reason}");
8744    }
8745
8746    #[test]
8747    fn validate_etiquetas_rejects_leading_digit_entry() {
8748        // Canonical paste-from-numbered-list footgun: the author
8749        // copied `1. mesh` from a numbered doc and the `1` leaked
8750        // into the tag.
8751        let c = caixa_with_etiquetas(vec!["1mesh"]);
8752        let err = c.validate_etiquetas().unwrap_err();
8753        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8754            panic!("expected EtiquetaInvalid, got {err:?}");
8755        };
8756        assert_eq!(etiqueta, "1mesh");
8757        assert!(reason.contains("digit"), "got: {reason}");
8758    }
8759
8760    #[test]
8761    fn validate_etiquetas_rejects_leading_hyphen_entry() {
8762        // Canonical kebab-leak footgun.
8763        let c = caixa_with_etiquetas(vec!["-foo"]);
8764        let err = c.validate_etiquetas().unwrap_err();
8765        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8766            panic!("expected EtiquetaInvalid, got {err:?}");
8767        };
8768        assert_eq!(etiqueta, "-foo");
8769        assert!(reason.contains('-'), "got: {reason}");
8770    }
8771
8772    #[test]
8773    fn validate_etiquetas_rejects_non_ascii_entry() {
8774        // Canonical paste-from-Unicode-doc footgun. Every legitimate
8775        // search tag is strict ASCII; raw non-ASCII silently
8776        // round-trips inconsistently across NFC/NFD normalization on
8777        // APFS / case-folding filesystems and breaks the Artifact Hub
8778        // keyword search index lookup.
8779        let c = caixa_with_etiquetas(vec!["café"]);
8780        let err = c.validate_etiquetas().unwrap_err();
8781        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8782            panic!("expected EtiquetaInvalid, got {err:?}");
8783        };
8784        assert_eq!(etiqueta, "café");
8785        assert!(reason.contains("non-ASCII"), "got: {reason}");
8786    }
8787
8788    #[test]
8789    fn validate_etiquetas_rejects_period_entry() {
8790        // Canonical namespace-confusion / version-suffix footgun
8791        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
8792        // excludes `.` from the continuation set even though the
8793        // sibling `:caracteristicas` axis (Cargo's feature-name
8794        // grammar) admits it. Tighter than the sibling axis, peer
8795        // with Cargo's own crates.io keyword shape.
8796        let c = caixa_with_etiquetas(vec!["http.1"]);
8797        let err = c.validate_etiquetas().unwrap_err();
8798        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
8799            panic!("expected EtiquetaInvalid, got {err:?}");
8800        };
8801        assert_eq!(etiqueta, "http.1");
8802        assert!(reason.contains('.'), "got: {reason}");
8803    }
8804
8805    #[test]
8806    fn validate_etiquetas_empty_takes_precedence_over_shape() {
8807        // Per-entry empty-first cascade pin: an entry that is both
8808        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
8809        // narrower "this entry has no value" structural defect
8810        // dominates the broader shape-predicate diagnostic). The
8811        // empty arm fires before the shape predicate is consulted,
8812        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
8813        // cascade established on the sibling universal-axis Vec<String>
8814        // surface.
8815        let c = caixa_with_etiquetas(vec![""]);
8816        let err = c.validate_etiquetas().unwrap_err();
8817        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
8818    }
8819
8820    #[test]
8821    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
8822        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
8823        // entry that is malformed surfaces `EtiquetaInvalid` even when
8824        // a later entry would have collided on duplicate. The
8825        // per-entry shape arm fires inside the same loop iteration as
8826        // the empty arm, before the seen-set insert at end-of-iteration
8827        // — structural per-entry defects dominate the cross-entry
8828        // uniqueness diagnostic. Mirrors the peer
8829        // `validate_autores_shape_takes_precedence_over_duplicate`.
8830        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
8831        let err = c.validate_etiquetas().unwrap_err();
8832        assert!(
8833            matches!(err, ManifestError::EtiquetaInvalid { .. }),
8834            "got {err:?}",
8835        );
8836    }
8837
8838    #[test]
8839    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
8840        // Diagnostic-shape pin on the new shape arm (peer with
8841        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
8842        // the rendered Display surfaces both the offending slot name
8843        // and the offending value verbatim, so a `feira lint` run
8844        // points the author at the exact `:etiquetas` entry to fix.
8845        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
8846        let rendered = c.validate_etiquetas().unwrap_err().to_string();
8847        assert!(
8848            rendered.contains(":etiquetas"),
8849            "diagnostic must name the offending slot: {rendered}",
8850        );
8851        assert!(
8852            rendered.contains("mesh\\nhttp"),
8853            "diagnostic must quote the offending value (debug-escaped): {rendered}",
8854        );
8855    }
8856
8857    #[test]
8858    fn validate_etiquetas_rejects_at_21_byte_boundary() {
8859        // The 20-byte cap pin — boundary-exceeding case rejected,
8860        // boundary-accepting case passes. Mirrors the peer
8861        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
8862        // side pin, surfaced at the per-axis caller so the cap
8863        // propagates through validate end-to-end. Constructed as a
8864        // single all-`a` token so only the cap arm fires.
8865        let max_ok = "a".repeat(20);
8866        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
8867        c.validate_etiquetas().unwrap();
8868        let too_long = "a".repeat(21);
8869        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
8870        let err = c.validate_etiquetas().unwrap_err();
8871        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
8872            panic!("expected EtiquetaInvalid, got {err:?}");
8873        };
8874        assert!(reason.contains("20"), "got: {reason}");
8875        assert!(reason.contains("21"), "got: {reason}");
8876    }
8877
8878    #[test]
8879    fn validate_etiquetas_accepts_canonical_shaped_forms() {
8880        // Positive control sweep: every canonical-shaped tag from the
8881        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
8882        // example fixtures plus the substrate-fixed tags caixa-helm
8883        // unions in at chart render. Drift between this list and the
8884        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
8885        // sweep surfaces here — one source of truth for the rule.
8886        let c = caixa_with_etiquetas(vec![
8887            "example",
8888            "aplicacao",
8889            "mesh",
8890            "ecommerce",
8891            "demo",
8892            "infrastructure",
8893            "aws",
8894            "akeyless",
8895            "pangea-native",
8896            "hello-world",
8897            "wasm",
8898            "rust",
8899            "tatara-lisp",
8900            "caixa-servico",
8901            "lareira",
8902        ]);
8903        c.validate_etiquetas().unwrap();
8904    }
8905
8906    // ── validate_autores — universal-axis maintainer shape ────────────
8907
8908    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
8909        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8910        c.autores = autores.into_iter().map(String::from).collect();
8911        c
8912    }
8913
8914    #[test]
8915    fn validate_autores_accepts_empty_list() {
8916        // The empty-list identity: `Caixa::template` emits `:autores ()`,
8917        // so the gate is non-disruptive against every existing manifest.
8918        let c = caixa_with_autores(vec![]);
8919        c.validate_autores().unwrap();
8920    }
8921
8922    #[test]
8923    fn validate_autores_accepts_canonical_forms() {
8924        // Positive control sweep: every canonical-shaped non-empty
8925        // distinct maintainer list passes — the hello-rio / checkout-
8926        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
8927        // multi-author shape downstream packaging surfaces emit.
8928        let c = caixa_with_autores(vec!["pleme-io"]);
8929        c.validate_autores().unwrap();
8930        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
8931        c.validate_autores().unwrap();
8932    }
8933
8934    #[test]
8935    fn validate_autores_rejects_empty_entry() {
8936        // Canonical paste-from-blank-doc footgun. Without the gate the
8937        // empty entry rendered as `maintainers: [{name: "", email: null}]`
8938        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
8939        // to.
8940        let c = caixa_with_autores(vec![""]);
8941        let err = c.validate_autores().unwrap_err();
8942        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8943    }
8944
8945    #[test]
8946    fn validate_autores_rejects_duplicate_entry() {
8947        // Canonical copy-paste-the-wrong-author footgun. Unlike the
8948        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
8949        // dedups the rendered `keywords:` array), the `maintainers:`
8950        // rendering has *no* dedup — duplicates stack verbatim. The
8951        // duplicate-arm names the offending author verbatim.
8952        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
8953        let err = c.validate_autores().unwrap_err();
8954        let ManifestError::AutorDuplicate { autor } = err else {
8955            panic!("expected AutorDuplicate, got {err:?}");
8956        };
8957        assert_eq!(autor, "pleme-io");
8958    }
8959
8960    #[test]
8961    fn validate_autores_empty_takes_precedence_over_duplicate() {
8962        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
8963        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
8964        // "this entry has no value" defect dominates the cross-entry
8965        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
8966        // cascades on `:etiquetas` (`EtiquetaEmpty` before
8967        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
8968        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
8969        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
8970        // `MembroDuplicate`).
8971        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
8972        let err = c.validate_autores().unwrap_err();
8973        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
8974    }
8975
8976    #[test]
8977    fn validate_autores_duplicate_reports_first_collision() {
8978        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
8979        // duplicate (the lexicographically-earliest offending position
8980        // — the second `"a"` at index 2 collides with the first `"a"`
8981        // at index 0), not the later `"b"` collision at index 3,
8982        // peer with every other first-collision diagnostic posture on
8983        // this surface.
8984        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
8985        let err = c.validate_autores().unwrap_err();
8986        let ManifestError::AutorDuplicate { autor } = err else {
8987            panic!("expected AutorDuplicate, got {err:?}");
8988        };
8989        assert_eq!(autor, "a");
8990    }
8991
8992    #[test]
8993    fn validate_autores_case_sensitive() {
8994        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
8995        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
8996        // / `:children :caixa` exact-string-match discipline.
8997        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
8998        c.validate_autores().unwrap();
8999    }
9000
9001    #[test]
9002    fn validate_autores_diagnostic_carries_offending_author() {
9003        // Diagnostic-shape pin (peer with
9004        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
9005        // error's Display surfaces the offending author verbatim, so a
9006        // `feira lint` run can render the diagnostic without re-parsing
9007        // and the author can grep their caixa.lisp for the offending
9008        // value.
9009        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9010        let rendered = c.validate_autores().unwrap_err().to_string();
9011        assert!(
9012            rendered.contains(":autores"),
9013            "diagnostic must name the offending slot: {rendered}",
9014        );
9015        assert!(
9016            rendered.contains("pleme-io"),
9017            "diagnostic must quote the offending author: {rendered}",
9018        );
9019    }
9020
9021    #[test]
9022    fn validate_autores_rejects_leading_whitespace_entry() {
9023        // Canonical paste-from-aligned-doc footgun. Without the shape
9024        // gate `" pleme-io"` silently passed validate and landed as a
9025        // YAML plain-style scalar with leading whitespace in the
9026        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
9027        // dumper trims leading whitespace from plain-style scalars, so
9028        // the authored space round-tripped inconsistently back through
9029        // `caixa.lisp`. Mirrors the peer
9030        // `validate_descricao_rejects_leading_whitespace`.
9031        let c = caixa_with_autores(vec![" pleme-io"]);
9032        let err = c.validate_autores().unwrap_err();
9033        let ManifestError::AutorInvalid { autor, reason } = err else {
9034            panic!("expected AutorInvalid, got {err:?}");
9035        };
9036        assert_eq!(autor, " pleme-io");
9037        assert!(reason.contains("whitespace"), "got: {reason}");
9038    }
9039
9040    #[test]
9041    fn validate_autores_rejects_trailing_whitespace_entry() {
9042        // Canonical paste-from-doc footgun.
9043        let c = caixa_with_autores(vec!["pleme-io "]);
9044        let err = c.validate_autores().unwrap_err();
9045        let ManifestError::AutorInvalid { autor, reason } = err else {
9046            panic!("expected AutorInvalid, got {err:?}");
9047        };
9048        assert_eq!(autor, "pleme-io ");
9049        assert!(reason.contains("whitespace"), "got: {reason}");
9050    }
9051
9052    #[test]
9053    fn validate_autores_rejects_embedded_newline_entry() {
9054        // Canonical paste-from-multiline-doc footgun — the author
9055        // pasted a multi-line block of author records into one
9056        // `:autores` entry instead of splitting into one entry per
9057        // author. Without the shape gate `"alice\nbob"` silently
9058        // passed validate and landed as a YAML-illegal multi-line
9059        // scalar in the rendered Chart.yaml `maintainers:` array.
9060        let c = caixa_with_autores(vec!["alice\nbob"]);
9061        let err = c.validate_autores().unwrap_err();
9062        let ManifestError::AutorInvalid { autor, reason } = err else {
9063            panic!("expected AutorInvalid, got {err:?}");
9064        };
9065        assert_eq!(autor, "alice\nbob");
9066        assert!(reason.contains("newline"), "got: {reason}");
9067    }
9068
9069    #[test]
9070    fn validate_autores_rejects_embedded_carriage_return_entry() {
9071        // Canonical paste-from-Windows-CRLF-doc footgun.
9072        let c = caixa_with_autores(vec!["alice\rbob"]);
9073        let err = c.validate_autores().unwrap_err();
9074        let ManifestError::AutorInvalid { autor, reason } = err else {
9075            panic!("expected AutorInvalid, got {err:?}");
9076        };
9077        assert_eq!(autor, "alice\rbob");
9078        assert!(reason.contains("carriage return"), "got: {reason}");
9079    }
9080
9081    #[test]
9082    fn validate_autores_rejects_embedded_tab_entry() {
9083        // Canonical tab-from-aligned-doc footgun.
9084        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
9085        let err = c.validate_autores().unwrap_err();
9086        let ManifestError::AutorInvalid { autor, reason } = err else {
9087            panic!("expected AutorInvalid, got {err:?}");
9088        };
9089        assert_eq!(autor, "Pleme\tContributors");
9090        assert!(reason.contains("tab"), "got: {reason}");
9091    }
9092
9093    #[test]
9094    fn validate_autores_rejects_embedded_control_bytes_entry() {
9095        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
9096        // surface the same control-byte arm.
9097        for entry in [
9098            "alice\x00bob",
9099            "alice\x07bob",
9100            "alice\x1bbob",
9101            "alice\x7fbob",
9102        ] {
9103            let c = caixa_with_autores(vec![entry]);
9104            let err = c.validate_autores().unwrap_err();
9105            let ManifestError::AutorInvalid { autor, reason } = err else {
9106                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
9107            };
9108            assert_eq!(autor, entry);
9109            assert!(
9110                reason.contains("control character"),
9111                "{entry:?} reason: {reason}",
9112            );
9113        }
9114    }
9115
9116    #[test]
9117    fn validate_autores_accepts_unicode_entry() {
9118        // Unicode positive control: realistic maintainer names carry
9119        // Unicode (`François`, `日本語`, `naïve`). The predicate must
9120        // round-trip Unicode losslessly, peer with the
9121        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
9122        // sweep.
9123        let c = caixa_with_autores(vec![
9124            "François Dupont",
9125            "日本語の名前",
9126            "naïve <naive@example.com>",
9127        ]);
9128        c.validate_autores().unwrap();
9129    }
9130
9131    #[test]
9132    fn validate_autores_empty_takes_precedence_over_shape() {
9133        // Per-entry empty-first cascade pin: an entry that is both
9134        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
9135        // "this entry has no value" structural defect dominates the
9136        // broader shape-predicate diagnostic). The empty arm fires
9137        // before the shape predicate is consulted, mirroring the peer
9138        // `validate_repositorio_empty_takes_precedence_over_shape`
9139        // cascade on the universal `Option<String>` siblings — and now
9140        // established on the Vec<String> per-entry surface.
9141        let c = caixa_with_autores(vec![""]);
9142        let err = c.validate_autores().unwrap_err();
9143        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9144    }
9145
9146    #[test]
9147    fn validate_autores_shape_takes_precedence_over_duplicate() {
9148        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9149        // entry that is malformed surfaces `AutorInvalid` even when a
9150        // later entry would have collided on duplicate. The per-entry
9151        // shape arm fires inside the same loop iteration as the empty
9152        // arm, before the seen-set insert at end-of-iteration —
9153        // structural per-entry defects dominate the cross-entry
9154        // uniqueness diagnostic.
9155        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
9156        let err = c.validate_autores().unwrap_err();
9157        assert!(
9158            matches!(err, ManifestError::AutorInvalid { .. }),
9159            "got {err:?}",
9160        );
9161    }
9162
9163    #[test]
9164    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
9165        // Diagnostic-shape pin on the new shape arm (peer with
9166        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
9167        // the rendered Display surfaces both the offending slot name
9168        // and the offending value verbatim, so a `feira lint` run
9169        // points the author at the exact `:autores` entry to fix.
9170        let c = caixa_with_autores(vec!["alice\nbob"]);
9171        let rendered = c.validate_autores().unwrap_err().to_string();
9172        assert!(
9173            rendered.contains(":autores"),
9174            "diagnostic must name the offending slot: {rendered}",
9175        );
9176        assert!(
9177            rendered.contains("alice\\nbob"),
9178            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9179        );
9180    }
9181
9182    #[test]
9183    fn validate_autores_rejects_at_129_byte_boundary() {
9184        // The 128-byte cap pin — boundary-exceeding case rejected,
9185        // boundary-accepting case passes. Mirrors the peer
9186        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
9187        // substrate-side pin, surfaced at the per-axis caller so the
9188        // cap propagates through validate end-to-end. Constructed as
9189        // a single all-`a` token so only the cap arm fires.
9190        let max_ok = "a".repeat(128);
9191        let c = caixa_with_autores(vec![max_ok.as_str()]);
9192        c.validate_autores().unwrap();
9193        let too_long = "a".repeat(129);
9194        let c = caixa_with_autores(vec![too_long.as_str()]);
9195        let err = c.validate_autores().unwrap_err();
9196        let ManifestError::AutorInvalid { reason, .. } = err else {
9197            panic!("expected AutorInvalid, got {err:?}");
9198        };
9199        assert!(reason.contains("128"), "got: {reason}");
9200        assert!(reason.contains("129"), "got: {reason}");
9201    }
9202
9203    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
9204
9205    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
9206        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9207        c.repositorio = repositorio.map(String::from);
9208        c
9209    }
9210
9211    #[test]
9212    fn validate_repositorio_accepts_none() {
9213        // The omit-the-slot identity: `:repositorio` is optional. The
9214        // gate is a no-op when the author didn't declare a value —
9215        // every caixa without a `:repositorio` line trivially passes,
9216        // and the substrate-side renderers fall back to their
9217        // documented placeholder (`caixa-helm`'s `home: None`,
9218        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
9219        // URL). Mirrors the peer `validate_restart_window_accepts_none`
9220        // posture on the other `Option<String>` Caixa slot.
9221        let c = caixa_with_repositorio(None);
9222        c.validate_repositorio().unwrap();
9223    }
9224
9225    #[test]
9226    fn validate_repositorio_accepts_canonical_forms() {
9227        // Positive control sweep across every documented `:repositorio`
9228        // authoring shape — the same union the shared
9229        // `crate::render::is_git_repo_url` predicate accepts and the
9230        // peer `:deps :fonte :repo` axis already routes through.
9231        // Covers the `github:` shorthand (the canonical pleme-io
9232        // convention used in the `:repositorio` field of every
9233        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
9234        // `examples/`), the `https://…` URL the README quickstart uses,
9235        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
9236        // `file://` URL schemes the shared predicate documents.
9237        for repo in [
9238            "github:pleme-io/hello-rio",
9239            "github:pleme-io/checkout",
9240            "https://github.com/pleme-io/hello-rio",
9241            "ssh://git@github.com/pleme-io/hello-rio.git",
9242            "git://github.com/pleme-io/hello-rio.git",
9243            "git@github.com:pleme-io/hello-rio.git",
9244            "file:///srv/pleme/hello-rio",
9245        ] {
9246            let c = caixa_with_repositorio(Some(repo));
9247            c.validate_repositorio()
9248                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
9249        }
9250    }
9251
9252    #[test]
9253    fn validate_repositorio_rejects_empty_some() {
9254        // Canonical paste-from-blank-doc footgun. The narrower
9255        // [`ManifestError::RepositorioEmpty`] arm fires before the
9256        // shape predicate is consulted, mirroring the empty-first
9257        // cascade every peer per-axis identity gate uses
9258        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9259        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
9260        // the empty `Some("")` silently passed the renderer's
9261        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9262        // on `None`) and landed as `home: ""` in `Chart.yaml` /
9263        // `url: ""` in the FluxCD `GitRepository`.
9264        let c = caixa_with_repositorio(Some(""));
9265        let err = c.validate_repositorio().unwrap_err();
9266        assert!(
9267            matches!(err, ManifestError::RepositorioEmpty),
9268            "got {err:?}",
9269        );
9270    }
9271
9272    #[test]
9273    fn validate_repositorio_rejects_whitespace() {
9274        // Paste-from-doc whitespace footgun. The shared
9275        // `is_git_repo_url` predicate refuses any whitespace byte; a
9276        // trailing space in a `:repositorio` value silently broke
9277        // `git clone '<value> '` at clone time. The diagnostic names
9278        // the offending value verbatim.
9279        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
9280        let err = c.validate_repositorio().unwrap_err();
9281        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
9282            panic!("expected RepositorioInvalid, got {err:?}");
9283        };
9284        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
9285    }
9286
9287    #[test]
9288    fn validate_repositorio_rejects_control_char() {
9289        // Paste-from-multiline-doc CRLF footgun — control characters
9290        // at the URL boundary are a class of subprocess-arg injection
9291        // and break git's URL parser at every porcelain entry point.
9292        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
9293        let err = c.validate_repositorio().unwrap_err();
9294        assert!(
9295            matches!(err, ManifestError::RepositorioInvalid { .. }),
9296            "got {err:?}",
9297        );
9298    }
9299
9300    #[test]
9301    fn validate_repositorio_rejects_leading_dash() {
9302        // Canonical CLI-argument-injection footgun: `git clone <repo>`
9303        // interprets a leading `-` as a CLI flag, so a
9304        // `-upload-pack=…` value escapes the subprocess argument
9305        // boundary. The shared predicate refuses every leading-`-`
9306        // shape at validate time.
9307        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
9308        let err = c.validate_repositorio().unwrap_err();
9309        assert!(
9310            matches!(err, ManifestError::RepositorioInvalid { .. }),
9311            "got {err:?}",
9312        );
9313    }
9314
9315    #[test]
9316    fn validate_repositorio_rejects_missing_colon_separator() {
9317        // The bare `org/repo` ambiguity footgun — `git clone` reads
9318        // a no-`:` form as a relative filesystem path rather than the
9319        // GitHub-shorthand expansion the author probably intended.
9320        // The shared predicate refuses every shape without a `:`
9321        // separator.
9322        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9323        let err = c.validate_repositorio().unwrap_err();
9324        assert!(
9325            matches!(err, ManifestError::RepositorioInvalid { .. }),
9326            "got {err:?}",
9327        );
9328    }
9329
9330    #[test]
9331    fn validate_repositorio_rejects_fragment_anchor() {
9332        // Paste-from-browser-address-bar footgun on the
9333        // `:repositorio` axis — an author copies a GitHub permalink
9334        // to a README section / line-permalink and forgets to trim
9335        // the `#fragment` tail. The shared `is_git_repo_url`
9336        // predicate refuses the byte at the URL-grammar layer
9337        // (libcurl strips the fragment before opening the
9338        // transport, so the byte rides verbatim into the rendered
9339        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
9340        // fields but is silently dropped on the wire — two
9341        // manifest variants whose values differ only in their
9342        // fragment anchor lock to two distinct rendered artifacts
9343        // for the byte-identical clone, defeating the THEORY.md
9344        // §V.2 render-determinism contract on the `:repositorio`
9345        // axis the peer `:fonte :repo` axis already closes).
9346        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
9347        let err = c.validate_repositorio().unwrap_err();
9348        let ManifestError::RepositorioInvalid {
9349            repositorio,
9350            reason,
9351        } = err
9352        else {
9353            panic!("expected RepositorioInvalid, got {err:?}");
9354        };
9355        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
9356        assert!(
9357            reason.contains("must not contain `#`"),
9358            "reason must surface the fragment-`#` arm, got {reason:?}"
9359        );
9360    }
9361
9362    #[test]
9363    fn validate_repositorio_rejects_query_string() {
9364        // Paste-from-browser-address-bar footgun on the
9365        // `:repositorio` axis (peer with the a68f818 fragment-`#`
9366        // arm on the same axis). An author copies a GitHub tab
9367        // deep-link out of the address bar and forgets to trim
9368        // the `?tab=…` query tail. The shared `is_git_repo_url`
9369        // predicate refuses the byte at the URL-grammar layer
9370        // (GitHub / GitLab / Bitbucket silently ignore the
9371        // `?query` tail and serve the same repo regardless, so
9372        // the byte rides verbatim into the rendered `Chart.yaml`
9373        // `home:` and FluxCD `GitRepository` `url:` fields but
9374        // is silently masked at the wire — two manifest variants
9375        // whose values differ only in their query tail lock to
9376        // two distinct rendered artifacts for the byte-identical
9377        // clone, defeating the THEORY.md §V.2 render-determinism
9378        // contract on the `:repositorio` axis the peer `:fonte
9379        // :repo` axis already closes).
9380        let c = caixa_with_repositorio(Some(
9381            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
9382        ));
9383        let err = c.validate_repositorio().unwrap_err();
9384        let ManifestError::RepositorioInvalid {
9385            repositorio,
9386            reason,
9387        } = err
9388        else {
9389            panic!("expected RepositorioInvalid, got {err:?}");
9390        };
9391        assert_eq!(
9392            repositorio,
9393            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
9394        );
9395        assert!(
9396            reason.contains("must not contain `?`"),
9397            "reason must surface the query-`?` arm, got {reason:?}"
9398        );
9399    }
9400
9401    #[test]
9402    fn validate_repositorio_rejects_embedded_backslash() {
9403        // Windows-file-path-confusion footgun on the `:repositorio`
9404        // axis (peer with the prior fragment-`#` / query-`?` arms on
9405        // the same axis, and peer with the new dep-level `:fonte :repo`
9406        // backslash arm on the URL-grammar trajectory). An author
9407        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
9408        // hello-rio` into the `:repositorio` slot, expecting the
9409        // `lareira-<nome>` chart's `home:` field and the FluxCD
9410        // `GitRepository` `url:` field to render the canonical local
9411        // file-URI. The shared `is_git_repo_url` predicate refuses
9412        // the byte at the URL-grammar layer (libcurl silently
9413        // translates `\` → `/` on some platforms and refuses it on
9414        // others, so the byte rides verbatim into the rendered
9415        // artifacts but is silently rewritten or rejected at the wire
9416        // — two manifest variants whose values differ only in
9417        // backslash-vs-forward-slash lock to two distinct rendered
9418        // artifacts for the byte-identical clone, defeating the
9419        // THEORY.md §V.2 render-determinism contract on the
9420        // `:repositorio` axis the peer `:fonte :repo` axis already
9421        // closes).
9422        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
9423        let err = c.validate_repositorio().unwrap_err();
9424        let ManifestError::RepositorioInvalid {
9425            repositorio,
9426            reason,
9427        } = err
9428        else {
9429            panic!("expected RepositorioInvalid, got {err:?}");
9430        };
9431        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
9432        assert!(
9433            reason.contains("must not contain `\\`"),
9434            "reason must surface the backslash-`\\` arm, got {reason:?}"
9435        );
9436    }
9437
9438    #[test]
9439    fn validate_repositorio_rejects_uri_template_placeholder() {
9440        // URI Template (RFC 6570) placeholder footgun on the
9441        // `:repositorio` axis (peer with the prior fragment-`#` /
9442        // query-`?` / backslash-`\` arms on the same axis, and peer
9443        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
9444        // URL-grammar trajectory). An author pastes a quick-start
9445        // README snippet / OpenAPI `servers:` URL / Helm chart
9446        // `home:` template carrying unresolved `{org}` / `{repo}`
9447        // placeholders into the `:repositorio` slot, expecting the
9448        // substrate to resolve the placeholder downstream. The
9449        // shared `is_git_repo_url` predicate refuses the byte at the
9450        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
9451        // `%7B` / `%7D` on the wire, so the byte round-trips
9452        // inconsistently between the rendered `Chart.yaml home:` /
9453        // FluxCD `GitRepository url:` and the resolver's `git clone`
9454        // invocation, defeating the THEORY.md §V.2 render-
9455        // determinism contract on the `:repositorio` axis the peer
9456        // `:fonte :repo` axis already closes; every git porcelain
9457        // entry-point additionally fetches a nonexistent literal-
9458        // `{placeholder}`-named path far from the source caixa.lisp).
9459        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
9460        let err = c.validate_repositorio().unwrap_err();
9461        let ManifestError::RepositorioInvalid {
9462            repositorio,
9463            reason,
9464        } = err
9465        else {
9466            panic!("expected RepositorioInvalid, got {err:?}");
9467        };
9468        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
9469        assert!(
9470            reason.contains("must not contain `{`"),
9471            "reason must surface the open-brace `{{` arm, got {reason:?}"
9472        );
9473        assert!(
9474            reason.contains("URI Template") || reason.contains("RFC 6570"),
9475            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
9476        );
9477    }
9478
9479    #[test]
9480    fn validate_repositorio_empty_takes_precedence_over_shape() {
9481        // Empty-first cascade pin: the empty `Some("")` surfaces the
9482        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
9483        // `RepositorioInvalid`, mirroring the peer
9484        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
9485        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
9486        // `is_git_repo_url` predicate also rejects the empty input
9487        // (defensively, with its own `"must not be empty"` reason),
9488        // but the manifest-layer empty arm runs first to surface the
9489        // narrower diagnostic verbatim.
9490        let c = caixa_with_repositorio(Some(""));
9491        let err = c.validate_repositorio().unwrap_err();
9492        assert!(
9493            matches!(err, ManifestError::RepositorioEmpty),
9494            "got {err:?}",
9495        );
9496    }
9497
9498    #[test]
9499    fn validate_repositorio_diagnostic_carries_offending_value() {
9500        // Diagnostic-shape pin (peer with
9501        // `validate_autores_diagnostic_carries_offending_author`): the
9502        // error's Display surfaces the offending value + slot name
9503        // verbatim, so a `feira lint` run can render the diagnostic
9504        // without re-parsing and the author can grep their caixa.lisp
9505        // for the offending `:repositorio` value.
9506        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
9507        let rendered = c.validate_repositorio().unwrap_err().to_string();
9508        assert!(
9509            rendered.contains(":repositorio"),
9510            "diagnostic must name the offending slot: {rendered}",
9511        );
9512        assert!(
9513            rendered.contains("pleme-io/hello-rio"),
9514            "diagnostic must quote the offending value: {rendered}",
9515        );
9516    }
9517
9518    // ── validate_descricao — universal-axis Chart.yaml description shape ──
9519
9520    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
9521        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9522        c.descricao = descricao.map(String::from);
9523        c
9524    }
9525
9526    #[test]
9527    fn validate_descricao_accepts_none() {
9528        // The omit-the-slot identity: `:descricao` is optional. The
9529        // gate is a no-op when the author didn't declare a value —
9530        // every caixa without a `:descricao` line trivially passes,
9531        // and the substrate-side renderers fall back to their
9532        // documented `caixa.nome`-derived placeholder. Mirrors the
9533        // peer `validate_repositorio_accepts_none` posture on the
9534        // sibling `Option<String>` Caixa slot.
9535        let c = caixa_with_descricao(None);
9536        c.validate_descricao().unwrap();
9537    }
9538
9539    #[test]
9540    fn validate_descricao_accepts_canonical_summary() {
9541        // Positive control: the canonical pleme-io descricao shape —
9542        // a short free-form prose summary — passes the gate. Covers
9543        // the fixture shapes the `caixa-helm` / `caixa-flux` /
9544        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
9545        // wasip2 caixa Servico."`, `"Checkout flow."`).
9546        for desc in [
9547            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9548            "Checkout flow.",
9549            "AWS provider caixa for tatara-lisp",
9550            "FIXME — describe this caixa",
9551            "x",
9552        ] {
9553            let c = caixa_with_descricao(Some(desc));
9554            c.validate_descricao()
9555                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
9556        }
9557    }
9558
9559    #[test]
9560    fn validate_descricao_rejects_empty_some() {
9561        // Canonical paste-from-blank-doc footgun. Without this gate
9562        // the empty `Some("")` silently passed the renderer's
9563        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
9564        // on `None`) and landed as `description: ""` in `Chart.yaml`
9565        // and a blank `README.md` header. Mirrors the peer
9566        // [`ManifestError::RepositorioEmpty`] empty-arm on the
9567        // sibling `Option<String>` Caixa slot.
9568        let c = caixa_with_descricao(Some(""));
9569        let err = c.validate_descricao().unwrap_err();
9570        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9571    }
9572
9573    #[test]
9574    fn validate_descricao_rejects_leading_whitespace() {
9575        // Paste-from-aligned-doc footgun: a leading ASCII space the
9576        // bare empty-arm gate accepted, the shape predicate now
9577        // refuses. The diagnostic carries the offending value
9578        // verbatim (with the leading space preserved) so the author
9579        // can grep their caixa.lisp for the exact `:descricao` line
9580        // and fix the round-trip-inconsistent leading whitespace.
9581        // Mirrors the peer
9582        // `validate_licenca_rejects_leading_whitespace` arm on the
9583        // sibling `:licenca` axis.
9584        let c = caixa_with_descricao(Some(" Checkout flow."));
9585        let err = c.validate_descricao().unwrap_err();
9586        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9587            panic!("expected DescricaoInvalid, got {err:?}");
9588        };
9589        assert_eq!(descricao, " Checkout flow.");
9590        assert!(reason.contains("whitespace"), "got: {reason:?}");
9591    }
9592
9593    #[test]
9594    fn validate_descricao_rejects_trailing_whitespace() {
9595        // Paste-from-doc footgun: a trailing ASCII space the bare
9596        // empty-arm gate accepted, the shape predicate now refuses.
9597        let c = caixa_with_descricao(Some("Checkout flow. "));
9598        let err = c.validate_descricao().unwrap_err();
9599        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
9600            panic!("expected DescricaoInvalid, got {err:?}");
9601        };
9602        assert_eq!(descricao, "Checkout flow. ");
9603        assert!(reason.contains("whitespace"), "got: {reason:?}");
9604    }
9605
9606    #[test]
9607    fn validate_descricao_rejects_embedded_newline() {
9608        // Paste-from-multiline-doc footgun: an embedded LF the bare
9609        // empty-arm gate accepted, the shape predicate now refuses.
9610        // Without this gate the embedded newline silently landed in
9611        // the rendered Chart.yaml as a multi-line YAML block scalar,
9612        // and every chart-aware UI (`helm list`, `helm search`,
9613        // Artifact Hub) renders the description in a single-line
9614        // column so the embedded newline is silently dropped at
9615        // every downstream consumer.
9616        let c = caixa_with_descricao(Some("Checkout\nflow."));
9617        let err = c.validate_descricao().unwrap_err();
9618        assert!(
9619            matches!(err, ManifestError::DescricaoInvalid { .. }),
9620            "got {err:?}",
9621        );
9622        assert!(err.to_string().contains("newline"), "got {err}");
9623    }
9624
9625    #[test]
9626    fn validate_descricao_rejects_embedded_carriage_return() {
9627        // Paste-from-Windows-CRLF-doc footgun.
9628        let c = caixa_with_descricao(Some("Checkout\rflow."));
9629        let err = c.validate_descricao().unwrap_err();
9630        assert!(
9631            matches!(err, ManifestError::DescricaoInvalid { .. }),
9632            "got {err:?}",
9633        );
9634        assert!(err.to_string().contains("carriage return"), "got {err}");
9635    }
9636
9637    #[test]
9638    fn validate_descricao_rejects_embedded_tab() {
9639        // Tab-from-aligned-doc footgun.
9640        let c = caixa_with_descricao(Some("Checkout\tflow."));
9641        let err = c.validate_descricao().unwrap_err();
9642        assert!(
9643            matches!(err, ManifestError::DescricaoInvalid { .. }),
9644            "got {err:?}",
9645        );
9646        assert!(err.to_string().contains("tab"), "got {err}");
9647    }
9648
9649    #[test]
9650    fn validate_descricao_rejects_embedded_control_bytes() {
9651        // Paste-from-binary-blob footgun: every other control byte
9652        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
9653        // the peer SPDX-expression control-byte arm.
9654        for s in [
9655            "Checkout\x00flow.",
9656            "Checkout\x07flow.",
9657            "Checkout\x1bflow.",
9658            "Checkout\x7fflow.",
9659        ] {
9660            let c = caixa_with_descricao(Some(s));
9661            let err = c.validate_descricao().unwrap_err();
9662            assert!(
9663                matches!(err, ManifestError::DescricaoInvalid { .. }),
9664                "{s:?} got {err:?}",
9665            );
9666            assert!(
9667                err.to_string().contains("control character"),
9668                "{s:?} got {err}",
9669            );
9670        }
9671    }
9672
9673    #[test]
9674    fn validate_descricao_accepts_unicode_prose() {
9675        // Positive control: Unicode prose is accepted — the
9676        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
9677        // and `Caixa::template`'s `"FIXME — describe this caixa"`
9678        // scaffold every `feira init` emits must continue to pass.
9679        for s in [
9680            "Canonical Rust→wasm32-wasip2 caixa Servico.",
9681            "FIXME — describe this caixa",
9682            "Caixa pour le projet tâche",
9683            "日本語の説明",
9684        ] {
9685            let c = caixa_with_descricao(Some(s));
9686            c.validate_descricao()
9687                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
9688        }
9689    }
9690
9691    #[test]
9692    fn validate_descricao_empty_takes_precedence_over_shape() {
9693        // Cascade pin: a `Some("")` surfaces the narrower
9694        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
9695        // shape-predicate arm. Mirrors the peer
9696        // `validate_licenca_empty_takes_precedence_over_shape` pin
9697        // on the sibling `:licenca` axis.
9698        let c = caixa_with_descricao(Some(""));
9699        let err = c.validate_descricao().unwrap_err();
9700        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
9701    }
9702
9703    #[test]
9704    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
9705        // Diagnostic-shape pin: the error's Display surfaces both
9706        // the `:descricao` slot name and the offending value
9707        // verbatim, so a `feira lint` run can render the diagnostic
9708        // without re-parsing and the author can grep their caixa.lisp
9709        // for the offending `:descricao` line. Mirrors the peer
9710        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
9711        // pin (ee2e888) on the sibling `:licenca` axis.
9712        // The `{descricao:?}` Debug format escapes embedded control
9713        // bytes; the quoted offending value surfaces as
9714        // `"Checkout\nflow."` (literal backslash-n) in the rendered
9715        // diagnostic. The author can grep their caixa.lisp for the
9716        // literal `Checkout` summary prefix.
9717        let c = caixa_with_descricao(Some("Checkout\nflow."));
9718        let rendered = c.validate_descricao().unwrap_err().to_string();
9719        assert!(
9720            rendered.contains(":descricao"),
9721            "diagnostic must name the offending slot: {rendered}",
9722        );
9723        assert!(
9724            rendered.contains("Checkout\\nflow."),
9725            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9726        );
9727    }
9728
9729    #[test]
9730    fn validate_descricao_template_passes() {
9731        // Round-trip pin: the bare `Caixa::template` shape carries
9732        // `:descricao "FIXME — describe this caixa"` (a non-empty
9733        // sentinel), so the template-derived Caixa passes the gate by
9734        // construction. A future template-shape change that omits or
9735        // empties `:descricao` would surface here as a regression.
9736        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9737        c.validate_descricao().unwrap();
9738    }
9739
9740    #[test]
9741    fn validate_descricao_diagnostic_names_offending_slot() {
9742        // Diagnostic-shape pin (peer with
9743        // `validate_repositorio_diagnostic_carries_offending_value`):
9744        // the error's Display surfaces the `:descricao` slot name
9745        // verbatim, so a `feira lint` run can render the diagnostic
9746        // without re-parsing and the author can grep their caixa.lisp
9747        // for the offending `:descricao` line.
9748        let c = caixa_with_descricao(Some(""));
9749        let rendered = c.validate_descricao().unwrap_err().to_string();
9750        assert!(
9751            rendered.contains(":descricao"),
9752            "diagnostic must name the offending slot: {rendered}",
9753        );
9754    }
9755
9756    // ── validate_licenca — universal-axis chart README license shape ──
9757
9758    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
9759        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9760        c.licenca = licenca.map(String::from);
9761        c
9762    }
9763
9764    #[test]
9765    fn validate_licenca_accepts_none() {
9766        // The omit-the-slot identity: `:licenca` is optional. The
9767        // gate is a no-op when the author didn't declare a value —
9768        // every caixa without a `:licenca` line trivially passes,
9769        // and the substrate-side `caixa-helm` renderer falls back to
9770        // the documented `"MIT"` placeholder. Mirrors the peer
9771        // `validate_descricao_accepts_none` posture on the sibling
9772        // `Option<String>` Caixa slot.
9773        let c = caixa_with_licenca(None);
9774        c.validate_licenca().unwrap();
9775    }
9776
9777    #[test]
9778    fn validate_licenca_accepts_canonical_expressions() {
9779        // Positive control: every canonical SPDX expression shape
9780        // pleme-io carries in its existing fixtures + the canonical
9781        // SPDX dual-license / with-exception / `+`-suffix / grouped /
9782        // user-defined-reference shapes all pass the gate. Covers
9783        // the single-license, `OR`-compound, `AND`-compound,
9784        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
9785        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
9786        // production the SPDX 2.1 expression grammar admits that
9787        // sits within the alphabet floor the
9788        // `is_spdx_expression_shape` predicate enforces.
9789        for lic in [
9790            "MIT",
9791            "Apache-2.0",
9792            "Apache-2.0 OR MIT",
9793            "Apache-2.0 AND MIT",
9794            "BSD-3-Clause",
9795            "MPL-2.0",
9796            "GPL-3.0-or-later",
9797            "GPL-2.0+",
9798            "Apache-2.0 WITH LLVM-exception",
9799            "(MIT OR Apache-2.0) AND BSD-3-Clause",
9800            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
9801            "LicenseRef-MyLicense",
9802            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
9803            "x",
9804        ] {
9805            let c = caixa_with_licenca(Some(lic));
9806            c.validate_licenca()
9807                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
9808        }
9809    }
9810
9811    #[test]
9812    fn validate_licenca_rejects_trailing_whitespace() {
9813        // Paste-from-doc whitespace footgun. A trailing space in the
9814        // `:licenca` value would silently break a downstream SPDX
9815        // parser that splits on exact `AND` / `OR` / `WITH` keyword
9816        // boundaries. The shape predicate refuses every trailing
9817        // whitespace byte by construction. Peer with
9818        // `validate_repositorio_rejects_whitespace` and
9819        // `validate_edicao_rejects_trailing_whitespace`.
9820        let c = caixa_with_licenca(Some("MIT "));
9821        let err = c.validate_licenca().unwrap_err();
9822        let ManifestError::LicencaInvalid { licenca, .. } = err else {
9823            panic!("expected LicencaInvalid, got {err:?}");
9824        };
9825        assert_eq!(licenca, "MIT ");
9826    }
9827
9828    #[test]
9829    fn validate_licenca_rejects_leading_whitespace() {
9830        // Symmetric paste-from-doc whitespace footgun on the leading
9831        // boundary — the gate refuses every shape that starts with a
9832        // space byte by construction. Peer with
9833        // `validate_edicao_rejects_leading_whitespace`.
9834        let c = caixa_with_licenca(Some(" MIT"));
9835        let err = c.validate_licenca().unwrap_err();
9836        assert!(
9837            matches!(err, ManifestError::LicencaInvalid { .. }),
9838            "got {err:?}",
9839        );
9840    }
9841
9842    #[test]
9843    fn validate_licenca_rejects_control_char() {
9844        // Paste-from-multiline-doc CRLF footgun — control characters
9845        // at the value boundary land as a malformed line in the
9846        // rendered chart `README.md` `## License` section. Peer with
9847        // `validate_repositorio_rejects_control_char` and
9848        // `validate_edicao_rejects_control_char`.
9849        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
9850            let c = caixa_with_licenca(Some(lic));
9851            let err = c.validate_licenca().unwrap_err();
9852            assert!(
9853                matches!(err, ManifestError::LicencaInvalid { .. }),
9854                "expected LicencaInvalid on {lic:?}, got {err:?}",
9855            );
9856        }
9857    }
9858
9859    #[test]
9860    fn validate_licenca_rejects_tab() {
9861        // Tab-from-aligned-doc footgun — SPDX expressions use a
9862        // single ASCII space between tokens; a tab breaks every
9863        // downstream SPDX parser that splits on exact `" "`
9864        // boundaries.
9865        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
9866        let err = c.validate_licenca().unwrap_err();
9867        assert!(
9868            matches!(err, ManifestError::LicencaInvalid { .. }),
9869            "got {err:?}",
9870        );
9871    }
9872
9873    #[test]
9874    fn validate_licenca_rejects_non_ascii() {
9875        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
9876        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
9877        // ".")` production. The shape predicate refuses every
9878        // non-ASCII byte by construction; peer with
9879        // `validate_edicao_rejects_non_ascii_lookalike`.
9880        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
9881            let c = caixa_with_licenca(Some(lic));
9882            let err = c.validate_licenca().unwrap_err();
9883            assert!(
9884                matches!(err, ManifestError::LicencaInvalid { .. }),
9885                "expected LicencaInvalid on {lic:?}, got {err:?}",
9886            );
9887        }
9888    }
9889
9890    #[test]
9891    fn validate_licenca_rejects_underscore() {
9892        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
9893        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
9894        // snake-case identifier conventions that don't apply to the
9895        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
9896        // "-" / "."`). The shape predicate refuses every underscore
9897        // byte by construction.
9898        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
9899            let c = caixa_with_licenca(Some(lic));
9900            let err = c.validate_licenca().unwrap_err();
9901            assert!(
9902                matches!(err, ManifestError::LicencaInvalid { .. }),
9903                "expected LicencaInvalid on {lic:?}, got {err:?}",
9904            );
9905        }
9906    }
9907
9908    #[test]
9909    fn validate_licenca_rejects_comma_separator() {
9910        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
9911        // SPDX expressions compose multiple licenses via `AND` / `OR`
9912        // keywords, not the comma separator. The shape predicate
9913        // refuses every comma byte by construction.
9914        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
9915            let c = caixa_with_licenca(Some(lic));
9916            let err = c.validate_licenca().unwrap_err();
9917            assert!(
9918                matches!(err, ManifestError::LicencaInvalid { .. }),
9919                "expected LicencaInvalid on {lic:?}, got {err:?}",
9920            );
9921        }
9922    }
9923
9924    #[test]
9925    fn validate_licenca_rejects_slash_dual_license() {
9926        // Slash-dual-license colloquial idiom footgun — the
9927        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
9928        // `package.license` field but non-SPDX; the SPDX equivalent
9929        // is `MIT OR Apache-2.0`. The shape predicate refuses every
9930        // forward-slash byte by construction.
9931        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
9932            let c = caixa_with_licenca(Some(lic));
9933            let err = c.validate_licenca().unwrap_err();
9934            assert!(
9935                matches!(err, ManifestError::LicencaInvalid { .. }),
9936                "expected LicencaInvalid on {lic:?}, got {err:?}",
9937            );
9938        }
9939    }
9940
9941    #[test]
9942    fn validate_licenca_rejects_semicolon_separator() {
9943        // Semicolon-list-separator confusion footgun — adjacent to
9944        // the comma-separator idiom, every list-separator-belongs-
9945        // to-list-grammar confusion lands here.
9946        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
9947        let err = c.validate_licenca().unwrap_err();
9948        assert!(
9949            matches!(err, ManifestError::LicencaInvalid { .. }),
9950            "got {err:?}",
9951        );
9952    }
9953
9954    #[test]
9955    fn validate_licenca_empty_takes_precedence_over_shape() {
9956        // Empty-first cascade pin: the empty `Some("")` surfaces the
9957        // narrower `LicencaEmpty` not the shape-predicate-wrapped
9958        // `LicencaInvalid`, mirroring the peer
9959        // `validate_edicao_empty_takes_precedence_over_shape` and
9960        // `validate_repositorio_empty_takes_precedence_over_shape`
9961        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
9962        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
9963        // The shape predicate also refuses the empty input
9964        // (defensively — `"must not be empty"`), but the manifest-
9965        // layer empty arm runs first to surface the narrower
9966        // diagnostic verbatim.
9967        let c = caixa_with_licenca(Some(""));
9968        let err = c.validate_licenca().unwrap_err();
9969        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
9970    }
9971
9972    #[test]
9973    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
9974        // Diagnostic-shape pin on the shape-predicate arm (peer with
9975        // `validate_edicao_invalid_diagnostic_carries_offending_value`
9976        // and `validate_repositorio_diagnostic_carries_offending_value`):
9977        // the error's Display surfaces the offending value + slot
9978        // name verbatim, so a `feira lint` run can render the
9979        // diagnostic without re-parsing and the author can grep
9980        // their caixa.lisp for the offending `:licenca` value.
9981        let c = caixa_with_licenca(Some("Apache_2.0"));
9982        let rendered = c.validate_licenca().unwrap_err().to_string();
9983        assert!(
9984            rendered.contains(":licenca"),
9985            "diagnostic must name the offending slot: {rendered}",
9986        );
9987        assert!(
9988            rendered.contains("Apache_2.0"),
9989            "diagnostic must quote the offending value: {rendered}",
9990        );
9991    }
9992
9993    #[test]
9994    fn validate_licenca_rejects_empty_some() {
9995        // Canonical paste-from-blank-doc footgun. Without this gate
9996        // the empty `Some("")` silently passed the renderer's
9997        // `Option::unwrap_or_else(|| "MIT".into())` (which only
9998        // fires on `None`) and landed as a bare trailing period in
9999        // the rendered chart `README.md` `## License` section.
10000        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
10001        // arm on the sibling `Option<String>` Caixa slot.
10002        let c = caixa_with_licenca(Some(""));
10003        let err = c.validate_licenca().unwrap_err();
10004        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10005    }
10006
10007    #[test]
10008    fn validate_licenca_template_passes() {
10009        // Round-trip pin: the bare `Caixa::template` shape (whether
10010        // it carries `:licenca` or omits it) passes the gate by
10011        // construction. A future template-shape change that
10012        // introduced `(:licenca "")` would surface here as a
10013        // regression. Mirrors the peer
10014        // `validate_descricao_template_passes` pin.
10015        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10016        c.validate_licenca().unwrap();
10017    }
10018
10019    #[test]
10020    fn validate_licenca_diagnostic_names_offending_slot() {
10021        // Diagnostic-shape pin (peer with
10022        // `validate_descricao_diagnostic_names_offending_slot`):
10023        // the error's Display surfaces the `:licenca` slot name
10024        // verbatim, so a `feira lint` run can render the diagnostic
10025        // without re-parsing and the author can grep their caixa.lisp
10026        // for the offending `:licenca` line.
10027        let c = caixa_with_licenca(Some(""));
10028        let rendered = c.validate_licenca().unwrap_err().to_string();
10029        assert!(
10030            rendered.contains(":licenca"),
10031            "diagnostic must name the offending slot: {rendered}",
10032        );
10033    }
10034
10035    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
10036
10037    #[test]
10038    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
10039        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
10040        // pin: [`Caixa::licenca`] must return the `:licenca` typed
10041        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10042        // raw `self.licenca.as_deref()` access across every
10043        // representative value in the accept-set — `None` (the "omit
10044        // the slot to defer to the caixa-helm renderer's `MIT`
10045        // fallback" arm every existing fixture without a `:licenca`
10046        // line carries), `Some("")` (a past-the-guard sentinel that
10047        // pins the accessor doesn't perform a silent
10048        // `Some("") → None` collapse on the empty arm — validate
10049        // rejects `Some("")` through `LicencaEmpty` but the accessor
10050        // must ship the raw slot verbatim so a validate-time gate
10051        // regression surfaces at the caixa-helm emit boundary rather
10052        // than being silently absorbed into the fallback), `Some("MIT")`
10053        // (the canonical single-license shape every `feira init`
10054        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
10055        // canonical `OR`-compound shape the peer
10056        // `validate_licenca_accepts_canonical_expressions` positive
10057        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
10058        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
10059        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
10060        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
10061        // guard sentinels — validate rejects each through
10062        // `LicencaInvalid` but the accessor must ship the raw slot
10063        // verbatim).
10064        //
10065        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
10066        // accessor pin on the substrate primitive — opens the "outer
10067        // [`Caixa`] `Option<&str>` scalar" projection pattern the
10068        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
10069        // future lifts fold on. Sibling in shape to the peer per-`:placement`
10070        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10071        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10072        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10073        // axes, extended onto the outer top-level [`Caixa`] universal-
10074        // axis surface. Pins against a future silent detour that
10075        // returned an owned `Option<String>` (which would type-check
10076        // but silently allocate on every accessor call, breaking the
10077        // zero-cost projection every peer sibling accessor carries), a
10078        // `Some("") → None` collapse (which would silently absorb the
10079        // `LicencaEmpty` refusal case at the accessor boundary and the
10080        // caixa-helm emit path would silently fall back to `"MIT"` on
10081        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
10082        // `None → Some("MIT")` collapse (which would silently reify
10083        // the caixa-helm renderer's `"MIT"` fallback at the accessor
10084        // boundary and every downstream consumer keying off the
10085        // `Option::is_none()` discriminator would lose the "author
10086        // omitted the slot" signal).
10087        for licenca in [
10088            None,
10089            Some(""),
10090            Some("MIT"),
10091            Some("Apache-2.0 OR MIT"),
10092            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
10093            Some("MIT "),
10094            Some(" MIT"),
10095            Some("MIT\n"),
10096            Some("Apache_2.0"),
10097            Some("MIT,Apache-2.0"),
10098        ] {
10099            let c = caixa_with_licenca(licenca);
10100            assert_eq!(
10101                c.licenca(),
10102                licenca,
10103                "Caixa::licenca must return :licenca verbatim (got {:?}, \
10104                 expected {licenca:?})",
10105                c.licenca(),
10106            );
10107            assert_eq!(
10108                c.licenca(),
10109                c.licenca.as_deref(),
10110                "Caixa::licenca must byte-equal the raw \
10111                 `self.licenca.as_deref()` field access across every \
10112                 value in the Option<&str> accept-set",
10113            );
10114        }
10115    }
10116
10117    #[test]
10118    fn validate_licenca_empty_arm_routes_through_accessor() {
10119        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
10120        // must key off [`Caixa::licenca`], not the raw
10121        // `self.licenca.as_deref()` field access. Structurally: a
10122        // `Caixa { licenca: Some(""), .. }` must surface the
10123        // `LicencaEmpty` refusal exactly, and a
10124        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
10125        // single-license form) must pass validate. The pair jointly
10126        // pins the accessor + validate-gate composition: any future
10127        // silent detour that had the accessor return `None` on the
10128        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10129        // silently absorb the `LicencaEmpty` refusal at the accessor
10130        // boundary and the validate gate would accept a struct-literal
10131        // `Caixa { licenca: Some(""), .. }` — the composition pin
10132        // catches that at caixa-core build time.
10133        //
10134        // Peer of the per-`:politicas :circuit-breaker`
10135        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
10136        // accessor-composition pin
10137        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
10138        // on the sibling per-M3-mesh-slot required-`u32` axis — same
10139        // "the validate / shape-gate predicate must route through the
10140        // substrate-primitive typed dispatch" discipline extended onto
10141        // the outer top-level [`Caixa`] universal-axis
10142        // `Option<&str>`-composition surface.
10143        let c = caixa_with_licenca(Some(""));
10144        assert!(
10145            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
10146            "validate_licenca must reject licenca == Some(\"\") with \
10147             LicencaEmpty — the accessor and the validate gate must \
10148             route through the same substrate-primitive typed dispatch \
10149             on the :licenca empty arm",
10150        );
10151        let c = caixa_with_licenca(Some("MIT"));
10152        assert!(
10153            c.validate_licenca().is_ok(),
10154            "validate_licenca must accept licenca == Some(\"MIT\") \
10155             (the canonical single-license SPDX shape)",
10156        );
10157    }
10158
10159    #[test]
10160    fn licenca_projects_option_str_by_borrow() {
10161        // The by-borrow pin: [`Caixa::licenca`] returns
10162        // `Option<&str>` by borrow — the `&str` borrows the underlying
10163        // `String` storage of the `Option<String>` slot and the
10164        // accessor must not allocate a fresh `String` on every call.
10165        // Peer of the per-`:placement`
10166        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10167        // borrow pin on the peer per-M3-mesh-slot
10168        // `Option<&str>`-return axis, extended onto the outer top-
10169        // level [`Caixa`] universal-axis `Option<&str>` shape — the
10170        // accessor's returned `&str` must borrow from `&self` (the
10171        // returned reference's lifetime is tied to `&self`), and
10172        // calling the accessor twice on the same [`Caixa`] must yield
10173        // the same `Option<&str>` verbatim (idempotent, no side
10174        // effects on `&self`).
10175        //
10176        // Pins against a future silent detour that returned an owned
10177        // `Option<String>` (which would type-check but silently
10178        // allocate on every call, breaking the zero-cost projection
10179        // every peer sibling accessor carries), or a one-arm-only
10180        // accessor that returned a saturating value on some sentinel
10181        // input (breaking the pass-through invariant the sibling
10182        // required-scalar accessors carry).
10183        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
10184            let c = caixa_with_licenca(licenca);
10185            let first = c.licenca();
10186            let second = c.licenca();
10187            assert_eq!(
10188                first, second,
10189                "Caixa::licenca must be idempotent — two successive \
10190                 calls on the same &self must return the same \
10191                 Option<&str>",
10192            );
10193            assert_eq!(
10194                first, licenca,
10195                "Caixa::licenca must return :licenca verbatim by \
10196                 borrow — got {first:?}, expected {licenca:?}",
10197            );
10198        }
10199    }
10200
10201    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
10202
10203    #[test]
10204    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
10205        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
10206        // pin: [`Caixa::repositorio`] must return the `:repositorio`
10207        // typed byte-string verbatim as an `Option<&str>`, byte-equal
10208        // to the raw `self.repositorio.as_deref()` access across every
10209        // representative value in the accept-set — `None` (the "omit
10210        // the slot to defer to the per-renderer placeholder" arm every
10211        // existing fixture without a `:repositorio` line carries),
10212        // `Some("")` (a past-the-guard sentinel that pins the accessor
10213        // doesn't perform a silent `Some("") → None` collapse on the
10214        // empty arm — validate rejects `Some("")` through
10215        // `RepositorioEmpty` but the accessor must ship the raw slot
10216        // verbatim so a validate-time gate regression surfaces at the
10217        // caixa-helm / caixa-flux emit boundary rather than being
10218        // silently absorbed into the per-renderer fallback),
10219        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
10220        // shorthand every existing manifest fixture across
10221        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
10222        // `Some("https://github.com/pleme-io/checkout")` (the canonical
10223        // `https://` URL the README quickstart uses),
10224        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
10225        // `Some("git://github.com/pleme-io/checkout.git")` /
10226        // `Some("git@github.com:pleme-io/checkout.git")` /
10227        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
10228        // github scheme the shared `is_git_repo_url` predicate
10229        // documents), and five past-the-guard sentinels for the
10230        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
10231        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
10232        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
10233        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
10234        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
10235        // sentinels pin the accessor doesn't silently absorb the
10236        // refusal cases into a fallback).
10237        //
10238        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
10239        // accessor pin on the substrate primitive — sibling of the peer
10240        // [`Caixa::licenca`] (6d5bc28) pin
10241        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
10242        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
10243        // projection pin pattern this pin folds on. Sibling in shape to
10244        // the peer per-`:placement`
10245        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10246        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10247        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10248        // axes, extended onto the outer top-level [`Caixa`] universal-
10249        // axis surface. Pins against a future silent detour that
10250        // returned an owned `Option<String>` (which would type-check
10251        // but silently allocate on every accessor call, breaking the
10252        // zero-cost projection every peer sibling accessor carries), a
10253        // `Some("") → None` collapse (which would silently absorb the
10254        // `RepositorioEmpty` refusal case at the accessor boundary and
10255        // the caixa-helm `Chart.yaml` `home:` fold would silently
10256        // render a `home: null` / omitted field on a struct-literal
10257        // `Caixa { repositorio: Some(""), .. }`), or a
10258        // `None → Some(<default>)` collapse (which would silently reify
10259        // the per-renderer fallback at the accessor boundary and every
10260        // downstream consumer keying off the `Option::is_none()`
10261        // discriminator would lose the "author omitted the slot"
10262        // signal).
10263        for repositorio in [
10264            None,
10265            Some(""),
10266            Some("github:pleme-io/hello-rio"),
10267            Some("https://github.com/pleme-io/checkout"),
10268            Some("ssh://git@github.com/pleme-io/checkout.git"),
10269            Some("git://github.com/pleme-io/checkout.git"),
10270            Some("git@github.com:pleme-io/checkout.git"),
10271            Some("file:///opt/mirrors/pleme-io/checkout"),
10272            Some("pleme-io/checkout"),
10273            Some("-upload-pack=evil"),
10274            Some("github:pleme-io/checkout?ref=main"),
10275            Some("github:pleme-io/checkout#main"),
10276            Some("github:pleme-io/{tpl}"),
10277        ] {
10278            let c = caixa_with_repositorio(repositorio);
10279            assert_eq!(
10280                c.repositorio(),
10281                repositorio,
10282                "Caixa::repositorio must return :repositorio verbatim \
10283                 (got {:?}, expected {repositorio:?})",
10284                c.repositorio(),
10285            );
10286            assert_eq!(
10287                c.repositorio(),
10288                c.repositorio.as_deref(),
10289                "Caixa::repositorio must byte-equal the raw \
10290                 `self.repositorio.as_deref()` field access across every \
10291                 value in the Option<&str> accept-set",
10292            );
10293        }
10294    }
10295
10296    #[test]
10297    fn validate_repositorio_empty_arm_routes_through_accessor() {
10298        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
10299        // gate must key off [`Caixa::repositorio`], not the raw
10300        // `self.repositorio.as_deref()` field access. Structurally: a
10301        // `Caixa { repositorio: Some(""), .. }` must surface the
10302        // `RepositorioEmpty` refusal exactly, and a
10303        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
10304        // (the canonical `github:` shorthand form) must pass validate.
10305        // The pair jointly pins the accessor + validate-gate
10306        // composition: any future silent detour that had the accessor
10307        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
10308        // collapse) would silently absorb the `RepositorioEmpty` refusal
10309        // at the accessor boundary and the validate gate would accept a
10310        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
10311        // composition pin catches that at caixa-core build time.
10312        //
10313        // Peer of the [`Caixa::licenca`] (6d5bc28)
10314        // `validate_licenca_empty_arm_routes_through_accessor`
10315        // composition pin on the sibling outer top-level [`Caixa`]
10316        // `Option<&str>` universal-axis surface — same "the validate /
10317        // shape-gate predicate must route through the substrate-
10318        // primitive typed dispatch" discipline extended onto the second
10319        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10320        // composition surface.
10321        let c = caixa_with_repositorio(Some(""));
10322        assert!(
10323            matches!(
10324                c.validate_repositorio(),
10325                Err(ManifestError::RepositorioEmpty),
10326            ),
10327            "validate_repositorio must reject repositorio == Some(\"\") \
10328             with RepositorioEmpty — the accessor and the validate gate \
10329             must route through the same substrate-primitive typed \
10330             dispatch on the :repositorio empty arm",
10331        );
10332        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
10333        assert!(
10334            c.validate_repositorio().is_ok(),
10335            "validate_repositorio must accept repositorio == \
10336             Some(\"github:pleme-io/hello-rio\") (the canonical \
10337             `github:` shorthand git-repo-URL shape)",
10338        );
10339    }
10340
10341    #[test]
10342    fn repositorio_projects_option_str_by_borrow() {
10343        // The by-borrow pin: [`Caixa::repositorio`] returns
10344        // `Option<&str>` by borrow — the `&str` borrows the underlying
10345        // `String` storage of the `Option<String>` slot and the
10346        // accessor must not allocate a fresh `String` on every call.
10347        // Peer of the per-`:placement`
10348        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
10349        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
10350        // `Option<&str>`-return axes, extended onto the second outer
10351        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
10352        // the accessor's returned `&str` must borrow from `&self` (the
10353        // returned reference's lifetime is tied to `&self`), and
10354        // calling the accessor twice on the same [`Caixa`] must yield
10355        // the same `Option<&str>` verbatim (idempotent, no side effects
10356        // on `&self`).
10357        //
10358        // Pins against a future silent detour that returned an owned
10359        // `Option<String>` (which would type-check but silently
10360        // allocate on every call, breaking the zero-cost projection
10361        // every peer sibling accessor carries), or a one-arm-only
10362        // accessor that returned a saturating value on some sentinel
10363        // input (breaking the pass-through invariant the sibling
10364        // required-scalar accessors carry).
10365        for repositorio in [
10366            None,
10367            Some(""),
10368            Some("github:pleme-io/hello-rio"),
10369            Some("https://github.com/pleme-io/checkout"),
10370        ] {
10371            let c = caixa_with_repositorio(repositorio);
10372            let first = c.repositorio();
10373            let second = c.repositorio();
10374            assert_eq!(
10375                first, second,
10376                "Caixa::repositorio must be idempotent — two successive \
10377                 calls on the same &self must return the same \
10378                 Option<&str>",
10379            );
10380            assert_eq!(
10381                first, repositorio,
10382                "Caixa::repositorio must return :repositorio verbatim by \
10383                 borrow — got {first:?}, expected {repositorio:?}",
10384            );
10385        }
10386    }
10387
10388    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
10389
10390    #[test]
10391    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
10392        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
10393        // return the author-declared `:repositorio` byte-string verbatim
10394        // on the `Some` arm — no scheme rewrite, no trailing-slash
10395        // canonicalization, no `github:` → `https://github.com/`
10396        // desugaring. The resolved-URL composer is the projection of
10397        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
10398        // the `String`-return arity every substrate-side field-fill
10399        // consumer keys off; on the `Some` arm the projection is
10400        // `str::to_owned` verbatim, so every accept-set value the
10401        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
10402        // across_permutations` pin covers (`https://…`, `github:…`,
10403        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
10404        // guard sentinel `pleme-io/…`) must survive the accessor
10405        // byte-equal. Pins against a future silent detour that rewrote
10406        // the `github:` shorthand to the `https://github.com/` full URL
10407        // at the accessor boundary (which would silently split the
10408        // resolved-URL surface from the raw [`Caixa::repositorio`]
10409        // accessor's documented pass-through invariant), or a trailing-
10410        // slash normalization (which would silently break the
10411        // FluxCD `GitRepository` `spec.url` byte-exact match every
10412        // downstream consumer keys the source-controller reconcile off).
10413        for repositorio in [
10414            "github:pleme-io/hello-rio",
10415            "https://github.com/pleme-io/checkout",
10416            "ssh://git@github.com/pleme-io/checkout.git",
10417            "git://github.com/pleme-io/checkout.git",
10418            "git@github.com:pleme-io/checkout.git",
10419            "file:///opt/mirrors/pleme-io/checkout",
10420        ] {
10421            let c = caixa_with_repositorio(Some(repositorio));
10422            assert_eq!(
10423                c.canonical_git_url(),
10424                repositorio,
10425                "Caixa::canonical_git_url on the Some arm must return \
10426                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
10427                c.canonical_git_url(),
10428            );
10429        }
10430    }
10431
10432    #[test]
10433    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
10434        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
10435        // `None` arm must emit the substrate's canonical pleme-org github
10436        // URL derived from `caixa.nome()` — `https://github.com/<org>/
10437        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
10438        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
10439        // is the exact byte-image of the prior inline
10440        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
10441        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
10442        // re-derived open-coded. Pins against a future silent detour
10443        // that migrated the `<org>` segment to a different constant (a
10444        // fork rebranding that split off a new
10445        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
10446        // to migrate onto), a scheme change (`https://` → `git://` or
10447        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
10448        // override (which would break the substrate-wide single-source-
10449        // of-truth guarantee this method encodes).
10450        let c = caixa_with_repositorio(None);
10451        let expected = format!(
10452            "https://github.com/{org}/{nome}",
10453            org = crate::DEFAULT_PLEME_GIT_ORG,
10454            nome = c.nome(),
10455        );
10456        assert_eq!(
10457            c.canonical_git_url(),
10458            expected,
10459            "Caixa::canonical_git_url on the None arm must fold through \
10460             the substrate's canonical pleme-org github URL fallback \
10461             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
10462             {:?}, expected {expected:?}",
10463            c.canonical_git_url(),
10464        );
10465    }
10466
10467    #[test]
10468    fn canonical_git_url_byte_matches_manual_composition() {
10469        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
10470        // byte-identically to the manual open-coded
10471        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
10472        //  format!("https://github.com/{org}/{nome}", ...))` composition
10473        // every prior substrate-side caller re-derived. Guards the
10474        // paired-site convergence just applied at caixa-flux's
10475        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
10476        // now routes through this accessor): a future implementation of
10477        // this method that reordered the format arguments, swapped the
10478        // `<org>` constant for a different one, or interposed a
10479        // canonicalization pass on the `Some` arm surfaces here as a
10480        // caixa-core build-time test failure rather than as a downstream
10481        // FluxCD `GitRepository` reconcile mismatch far from this
10482        // method's source.
10483        for repositorio in [
10484            None,
10485            Some("github:pleme-io/hello-rio"),
10486            Some("https://github.com/pleme-io/checkout"),
10487            Some("ssh://git@github.com/pleme-io/checkout.git"),
10488        ] {
10489            let c = caixa_with_repositorio(repositorio);
10490            let manual = c.repositorio().map_or_else(
10491                || {
10492                    format!(
10493                        "https://github.com/{org}/{nome}",
10494                        org = crate::DEFAULT_PLEME_GIT_ORG,
10495                        nome = c.nome(),
10496                    )
10497                },
10498                str::to_owned,
10499            );
10500            assert_eq!(
10501                c.canonical_git_url(),
10502                manual,
10503                "Caixa::canonical_git_url must byte-equal the manual \
10504                 open-coded `repositorio().map(str::to_owned)\
10505                 .unwrap_or_else(|| format!(...))` composition across \
10506                 every representative :repositorio input — got {:?}, \
10507                 expected {manual:?}",
10508                c.canonical_git_url(),
10509            );
10510        }
10511    }
10512
10513    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
10514
10515    #[test]
10516    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
10517        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
10518        // pin: [`Caixa::descricao`] must return the `:descricao` typed
10519        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10520        // raw `self.descricao.as_deref()` access across every
10521        // representative value in the accept-set — `None` (the "omit
10522        // the slot to defer to the per-renderer `caixa.nome`-derived
10523        // fallback" arm every existing fixture without a `:descricao`
10524        // line carries), `Some("")` (a past-the-guard sentinel that
10525        // pins the accessor doesn't perform a silent `Some("") → None`
10526        // collapse on the empty arm — validate rejects `Some("")`
10527        // through `DescricaoEmpty` but the accessor must ship the raw
10528        // slot verbatim so a validate-time gate regression surfaces at
10529        // the caixa-helm / caixa-feira emit boundary rather than being
10530        // silently absorbed into the per-renderer `caixa.nome`-derived
10531        // fallback), `Some("Checkout flow.")` (the canonical one-line
10532        // prose descriptor the peer
10533        // `validate_descricao_accepts_canonical_value` positive sweep
10534        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
10535        // Servico.")` (the multi-byte Unicode continuation-byte shape
10536        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
10537        // multi-glyph Unicode shape the peer
10538        // `is_chart_description_shape` predicate accepts), and five
10539        // past-the-guard sentinels for the `DescricaoInvalid` refusal
10540        // cases (`Some(" Checkout flow.")` leading-whitespace,
10541        // `Some("Checkout flow. ")` trailing-whitespace,
10542        // `Some("Checkout\nflow.")` embedded-LF,
10543        // `Some("Checkout\tflow.")` embedded-TAB, and
10544        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
10545        // the accessor doesn't silently absorb the refusal cases into
10546        // a fallback).
10547        //
10548        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
10549        // accessor pin on the substrate primitive — sibling of the peer
10550        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
10551        // (cc7332d) pins that opened the "outer [`Caixa`]
10552        // `Option<&str>` scalar" projection pin pattern this pin folds
10553        // on. Sibling in shape to the peer per-`:placement`
10554        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10555        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10556        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10557        // axes, extended onto the outer top-level [`Caixa`] universal-
10558        // axis surface. Pins against a future silent detour that
10559        // returned an owned `Option<String>` (which would type-check
10560        // but silently allocate on every accessor call, breaking the
10561        // zero-cost projection every peer sibling accessor carries), a
10562        // `Some("") → None` collapse (which would silently absorb the
10563        // `DescricaoEmpty` refusal case at the accessor boundary and
10564        // the caixa-helm `Chart.yaml` `description:` fold would
10565        // silently render a `caixa.nome`-derived fallback on a
10566        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
10567        // `None → Some(<default>)` collapse (which would silently
10568        // reify the per-renderer `caixa.nome`-derived fallback at the
10569        // accessor boundary and every downstream consumer keying off
10570        // the `Option::is_none()` discriminator would lose the "author
10571        // omitted the slot" signal).
10572        for descricao in [
10573            None,
10574            Some(""),
10575            Some("Checkout flow."),
10576            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10577            Some("→ — · ✓"),
10578            Some(" Checkout flow."),
10579            Some("Checkout flow. "),
10580            Some("Checkout\nflow."),
10581            Some("Checkout\tflow."),
10582            Some("Checkout\x00flow."),
10583        ] {
10584            let c = caixa_with_descricao(descricao);
10585            assert_eq!(
10586                c.descricao(),
10587                descricao,
10588                "Caixa::descricao must return :descricao verbatim (got \
10589                 {:?}, expected {descricao:?})",
10590                c.descricao(),
10591            );
10592            assert_eq!(
10593                c.descricao(),
10594                c.descricao.as_deref(),
10595                "Caixa::descricao must byte-equal the raw \
10596                 `self.descricao.as_deref()` field access across every \
10597                 value in the Option<&str> accept-set",
10598            );
10599        }
10600    }
10601
10602    #[test]
10603    fn validate_descricao_empty_arm_routes_through_accessor() {
10604        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
10605        // gate must key off [`Caixa::descricao`], not the raw
10606        // `self.descricao.as_deref()` field access. Structurally: a
10607        // `Caixa { descricao: Some(""), .. }` must surface the
10608        // `DescricaoEmpty` refusal exactly, and a
10609        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
10610        // canonical one-line-prose form) must pass validate. The pair
10611        // jointly pins the accessor + validate-gate composition: any
10612        // future silent detour that had the accessor return `None` on
10613        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
10614        // silently absorb the `DescricaoEmpty` refusal at the accessor
10615        // boundary and the validate gate would accept a struct-literal
10616        // `Caixa { descricao: Some(""), .. }` — the composition pin
10617        // catches that at caixa-core build time.
10618        //
10619        // Peer of the [`Caixa::licenca`] (6d5bc28)
10620        // `validate_licenca_empty_arm_routes_through_accessor` and
10621        // [`Caixa::repositorio`] (cc7332d)
10622        // `validate_repositorio_empty_arm_routes_through_accessor`
10623        // composition pins on the sibling outer top-level [`Caixa`]
10624        // `Option<&str>` universal-axis surface — same "the validate /
10625        // shape-gate predicate must route through the substrate-
10626        // primitive typed dispatch" discipline extended onto the third
10627        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
10628        // composition surface.
10629        let c = caixa_with_descricao(Some(""));
10630        assert!(
10631            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
10632            "validate_descricao must reject descricao == Some(\"\") \
10633             with DescricaoEmpty — the accessor and the validate gate \
10634             must route through the same substrate-primitive typed \
10635             dispatch on the :descricao empty arm",
10636        );
10637        let c = caixa_with_descricao(Some("Checkout flow."));
10638        assert!(
10639            c.validate_descricao().is_ok(),
10640            "validate_descricao must accept descricao == \
10641             Some(\"Checkout flow.\") (the canonical one-line-prose \
10642             chart-description shape)",
10643        );
10644    }
10645
10646    #[test]
10647    fn descricao_projects_option_str_by_borrow() {
10648        // The by-borrow pin: [`Caixa::descricao`] returns
10649        // `Option<&str>` by borrow — the `&str` borrows the underlying
10650        // `String` storage of the `Option<String>` slot and the
10651        // accessor must not allocate a fresh `String` on every call.
10652        // Peer of the [`Caixa::licenca`] (6d5bc28) and
10653        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
10654        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
10655        // the per-`:placement`
10656        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
10657        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
10658        // return axis, extended onto the third outer top-level
10659        // [`Caixa`] universal-axis `Option<&str>` shape — the
10660        // accessor's returned `&str` must borrow from `&self` (the
10661        // returned reference's lifetime is tied to `&self`), and
10662        // calling the accessor twice on the same [`Caixa`] must yield
10663        // the same `Option<&str>` verbatim (idempotent, no side
10664        // effects on `&self`).
10665        //
10666        // Pins against a future silent detour that returned an owned
10667        // `Option<String>` (which would type-check but silently
10668        // allocate on every call, breaking the zero-cost projection
10669        // every peer sibling accessor carries), or a one-arm-only
10670        // accessor that returned a saturating value on some sentinel
10671        // input (breaking the pass-through invariant the sibling
10672        // required-scalar accessors carry).
10673        for descricao in [
10674            None,
10675            Some(""),
10676            Some("Checkout flow."),
10677            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
10678        ] {
10679            let c = caixa_with_descricao(descricao);
10680            let first = c.descricao();
10681            let second = c.descricao();
10682            assert_eq!(
10683                first, second,
10684                "Caixa::descricao must be idempotent — two successive \
10685                 calls on the same &self must return the same \
10686                 Option<&str>",
10687            );
10688            assert_eq!(
10689                first, descricao,
10690                "Caixa::descricao must return :descricao verbatim by \
10691                 borrow — got {first:?}, expected {descricao:?}",
10692            );
10693        }
10694    }
10695
10696    // ── validate_edicao — universal-axis language-edition shape ──
10697
10698    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
10699        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10700        c.edicao = edicao.map(String::from);
10701        c
10702    }
10703
10704    #[test]
10705    fn validate_edicao_accepts_none() {
10706        // The omit-the-slot identity: `:edicao` is optional. The
10707        // gate is a no-op when the author didn't declare a value —
10708        // every caixa without an `:edicao` line trivially passes,
10709        // and the substrate-side build pipeline falls back to the
10710        // documented default edition. Mirrors the peer
10711        // `validate_licenca_accepts_none` posture on the sibling
10712        // `Option<String>` Caixa slot.
10713        let c = caixa_with_edicao(None);
10714        c.validate_edicao().unwrap();
10715    }
10716
10717    #[test]
10718    fn validate_edicao_accepts_canonical_value() {
10719        // Positive control: the canonical `"2026"` edition every
10720        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
10721        // `caixa-mesh`) carries by construction passes the gate.
10722        // Future-introduced sibling editions (`"2027"`, `"2030"`,
10723        // `"2049"`) that match the same 4-digit ASCII decimal year
10724        // shape must also trivially pass — the structural shape
10725        // predicate accepts every well-formed year regardless of
10726        // whether the substrate yet understands the specific value
10727        // (a future known-edition allowlist tightens that).
10728        for ed in ["2026", "2027", "2030", "2049"] {
10729            let c = caixa_with_edicao(Some(ed));
10730            c.validate_edicao()
10731                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
10732        }
10733    }
10734
10735    #[test]
10736    fn validate_edicao_rejects_empty_some() {
10737        // Canonical paste-from-blank-doc footgun. Without this gate
10738        // the empty `Some("")` silently lands as `(:edicao "")` in
10739        // the rendered caixa.lisp and a future renderer-side
10740        // consumer's `Option::unwrap_or_else` (which only fires on
10741        // `None`) skips its fallback. Mirrors the peer
10742        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
10743        // `Option<String>` Caixa slot.
10744        let c = caixa_with_edicao(Some(""));
10745        let err = c.validate_edicao().unwrap_err();
10746        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10747    }
10748
10749    #[test]
10750    fn validate_edicao_rejects_free_form_non_year() {
10751        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
10752        // `"nightly"` shapes carry no operational meaning on the
10753        // substrate's build-time edition selector. Until this gate
10754        // landed the bare empty-arm check let every such value
10755        // through and broke far from the source caixa.lisp. Peer
10756        // with the shape-predicate cascade
10757        // `validate_repositorio_rejects_missing_colon_separator`
10758        // establishes past its own empty arm.
10759        for ed in ["x", "latest", "nightly", "stable"] {
10760            let c = caixa_with_edicao(Some(ed));
10761            let err = c.validate_edicao().unwrap_err();
10762            assert!(
10763                matches!(err, ManifestError::EdicaoInvalid { .. }),
10764                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10765            );
10766        }
10767    }
10768
10769    #[test]
10770    fn validate_edicao_rejects_trailing_whitespace() {
10771        // Paste-from-doc whitespace footgun. A trailing space in
10772        // the `:edicao` value would silently break the substrate's
10773        // build-time edition match-table lookup at the rendered
10774        // artifact's edition-selector consumer. The shape predicate
10775        // refuses every whitespace byte by construction (any byte
10776        // outside `0-9` fails `is_ascii_digit`). Peer with
10777        // `validate_repositorio_rejects_whitespace`.
10778        let c = caixa_with_edicao(Some("2026 "));
10779        let err = c.validate_edicao().unwrap_err();
10780        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
10781            panic!("expected EdicaoInvalid, got {err:?}");
10782        };
10783        assert_eq!(edicao, "2026 ");
10784    }
10785
10786    #[test]
10787    fn validate_edicao_rejects_leading_whitespace() {
10788        // Symmetric paste-from-doc whitespace footgun on the leading
10789        // boundary — the gate refuses every shape with a non-digit
10790        // byte by construction.
10791        let c = caixa_with_edicao(Some(" 2026"));
10792        let err = c.validate_edicao().unwrap_err();
10793        assert!(
10794            matches!(err, ManifestError::EdicaoInvalid { .. }),
10795            "got {err:?}",
10796        );
10797    }
10798
10799    #[test]
10800    fn validate_edicao_rejects_control_char() {
10801        // Paste-from-multiline-doc CRLF footgun — control characters
10802        // at the value boundary break the substrate's build-time
10803        // edition-selector parser. Peer with
10804        // `validate_repositorio_rejects_control_char`.
10805        let c = caixa_with_edicao(Some("2026\n"));
10806        let err = c.validate_edicao().unwrap_err();
10807        assert!(
10808            matches!(err, ManifestError::EdicaoInvalid { .. }),
10809            "got {err:?}",
10810        );
10811    }
10812
10813    #[test]
10814    fn validate_edicao_rejects_non_ascii_lookalike() {
10815        // Fullwidth-keyboard look-alike footgun — `"2026"` is
10816        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
10817        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
10818        // edition selector wants an ASCII year, and the gate
10819        // refuses every non-ASCII shape by construction (length in
10820        // bytes is 12 ≠ 4, *and* every byte falls outside
10821        // `is_ascii_digit`'s `0-9` range).
10822        let c = caixa_with_edicao(Some("2026"));
10823        let err = c.validate_edicao().unwrap_err();
10824        assert!(
10825            matches!(err, ManifestError::EdicaoInvalid { .. }),
10826            "got {err:?}",
10827        );
10828    }
10829
10830    #[test]
10831    fn validate_edicao_rejects_version_tag_prefix() {
10832        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
10833        // / `"r2026"` are familiar shapes from git-tag / Rust
10834        // edition / release-tag conventions that don't apply to
10835        // the year-shaped edition axis. The shape predicate refuses
10836        // every leading non-digit prefix.
10837        for ed in ["v2026", "e2026", "r2026"] {
10838            let c = caixa_with_edicao(Some(ed));
10839            let err = c.validate_edicao().unwrap_err();
10840            assert!(
10841                matches!(err, ManifestError::EdicaoInvalid { .. }),
10842                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10843            );
10844        }
10845    }
10846
10847    #[test]
10848    fn validate_edicao_rejects_decimal_shape() {
10849        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
10850        // `"2026.0"` are familiar shapes from semver / float
10851        // conventions that don't apply to the year-shaped edition
10852        // axis. The shape predicate refuses every non-digit byte
10853        // (`.` falls outside `is_ascii_digit`).
10854        for ed in ["2026.1", "2026.0", "2026.0.1"] {
10855            let c = caixa_with_edicao(Some(ed));
10856            let err = c.validate_edicao().unwrap_err();
10857            assert!(
10858                matches!(err, ManifestError::EdicaoInvalid { .. }),
10859                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10860            );
10861        }
10862    }
10863
10864    #[test]
10865    fn validate_edicao_rejects_wrong_length_numeric() {
10866        // Wrong-length numeric footgun — `"26"` (truncated) /
10867        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
10868        // (zero-padded too wide) all parse as integers but don't
10869        // name a 4-digit year. The shape predicate refuses every
10870        // value whose length isn't exactly 4 bytes.
10871        for ed in ["26", "202", "20260", "00026", "9"] {
10872            let c = caixa_with_edicao(Some(ed));
10873            let err = c.validate_edicao().unwrap_err();
10874            assert!(
10875                matches!(err, ManifestError::EdicaoInvalid { .. }),
10876                "expected EdicaoInvalid on {ed:?}, got {err:?}",
10877            );
10878        }
10879    }
10880
10881    #[test]
10882    fn validate_edicao_empty_takes_precedence_over_shape() {
10883        // Empty-first cascade pin: the empty `Some("")` surfaces
10884        // the narrower `EdicaoEmpty` not the shape-predicate-
10885        // wrapped `EdicaoInvalid`, mirroring the peer
10886        // `validate_repositorio_empty_takes_precedence_over_shape`
10887        // (`RepositorioEmpty` → `RepositorioInvalid`),
10888        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
10889        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
10890        // cascades. The shape predicate also refuses the empty
10891        // input (defensively — `s.len() != 4`), but the
10892        // manifest-layer empty arm runs first to surface the
10893        // narrower diagnostic verbatim.
10894        let c = caixa_with_edicao(Some(""));
10895        let err = c.validate_edicao().unwrap_err();
10896        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
10897    }
10898
10899    #[test]
10900    fn validate_edicao_template_passes() {
10901        // Round-trip pin: the bare `Caixa::template` shape (which
10902        // carries `:edicao "2026"` verbatim) passes the gate by
10903        // construction. A future template-shape change that
10904        // introduced `(:edicao "")` or a non-year value would
10905        // surface here as a regression. Mirrors the peer
10906        // `validate_licenca_template_passes` pin.
10907        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10908        c.validate_edicao().unwrap();
10909    }
10910
10911    #[test]
10912    fn validate_edicao_diagnostic_names_offending_slot() {
10913        // Diagnostic-shape pin (peer with
10914        // `validate_licenca_diagnostic_names_offending_slot`): the
10915        // error's Display surfaces the `:edicao` slot name verbatim,
10916        // so a `feira lint` run can render the diagnostic without
10917        // re-parsing and the author can grep their caixa.lisp for
10918        // the offending `:edicao` line.
10919        let c = caixa_with_edicao(Some(""));
10920        let rendered = c.validate_edicao().unwrap_err().to_string();
10921        assert!(
10922            rendered.contains(":edicao"),
10923            "diagnostic must name the offending slot: {rendered}",
10924        );
10925    }
10926
10927    #[test]
10928    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
10929        // Diagnostic-shape pin on the shape-predicate arm (peer
10930        // with `validate_repositorio_diagnostic_carries_offending_value`):
10931        // the error's Display surfaces the offending value + slot
10932        // name verbatim, so a `feira lint` run can render the
10933        // diagnostic without re-parsing and the author can grep
10934        // their caixa.lisp for the offending `:edicao` value.
10935        let c = caixa_with_edicao(Some("v2026"));
10936        let rendered = c.validate_edicao().unwrap_err().to_string();
10937        assert!(
10938            rendered.contains(":edicao"),
10939            "diagnostic must name the offending slot: {rendered}",
10940        );
10941        assert!(
10942            rendered.contains("v2026"),
10943            "diagnostic must quote the offending value: {rendered}",
10944        );
10945    }
10946
10947    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
10948
10949    #[test]
10950    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
10951        // The canonical per-`Caixa` `:edicao` language-edition scalar
10952        // pin: [`Caixa::edicao`] must return the `:edicao` typed
10953        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10954        // raw `self.edicao.as_deref()` access across every representative
10955        // value in the accept-set — `None` (the "omit the slot to defer
10956        // to the substrate's default edition" arm every existing
10957        // [`caixa-resolver`] fixture without an `:edicao` line carries),
10958        // `Some("")` (a past-the-guard sentinel that pins the accessor
10959        // doesn't perform a silent `Some("") → None` collapse on the
10960        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
10961        // but the accessor must ship the raw slot verbatim so a
10962        // validate-time gate regression surfaces at any future edition-
10963        // aware consumer's boundary rather than being silently absorbed
10964        // into the substrate's default edition), `Some("2026")` (the
10965        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
10966        // template scaffolds via [`Caixa::template`] and every
10967        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
10968        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
10969        // carries by construction), `Some("2018")` / `Some("2021")` /
10970        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
10971        // peer with Cargo's `[package] edition` grammar every future-
10972        // introduced sibling to `"2026"` will follow), and eight
10973        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
10974        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
10975        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
10976        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
10977        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
10978        // length-numeric, `Some("latest")` free-form-non-year — the
10979        // sentinels pin the accessor doesn't silently absorb the
10980        // refusal cases into a substrate-default-edition fallback).
10981        //
10982        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
10983        // return scalar accessor pin on the substrate primitive —
10984        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
10985        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
10986        // (3f16e2f) pins that opened the "outer [`Caixa`]
10987        // `Option<&str>` scalar" projection pin pattern this pin folds
10988        // on. Sibling in shape to the peer per-`:placement`
10989        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
10990        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
10991        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
10992        // axes, extended onto the outer top-level [`Caixa`] universal-
10993        // axis surface's last unlifted `Option<String>` slot. Pins
10994        // against a future silent detour that returned an owned
10995        // `Option<String>` (which would type-check but silently
10996        // allocate on every accessor call, breaking the zero-cost
10997        // projection every peer sibling accessor carries), a
10998        // `Some("") → None` collapse (which would silently absorb the
10999        // `EdicaoEmpty` refusal case at the accessor boundary and any
11000        // future edition-aware consumer would silently fall back to
11001        // the substrate's default edition on a struct-literal
11002        // `Caixa { edicao: Some(""), .. }`), or a
11003        // `None → Some("2026")` collapse (which would silently reify
11004        // the substrate's default edition at the accessor boundary
11005        // and every downstream consumer keying off the
11006        // `Option::is_none()` discriminator would lose the "author
11007        // omitted the slot" signal).
11008        for edicao in [
11009            None,
11010            Some(""),
11011            Some("2026"),
11012            Some("2018"),
11013            Some("2021"),
11014            Some("2024"),
11015            Some("2026 "),
11016            Some(" 2026"),
11017            Some("2026\n"),
11018            Some("2026"),
11019            Some("v2026"),
11020            Some("2026.1"),
11021            Some("26"),
11022            Some("latest"),
11023        ] {
11024            let c = caixa_with_edicao(edicao);
11025            assert_eq!(
11026                c.edicao(),
11027                edicao,
11028                "Caixa::edicao must return :edicao verbatim (got {:?}, \
11029                 expected {edicao:?})",
11030                c.edicao(),
11031            );
11032            assert_eq!(
11033                c.edicao(),
11034                c.edicao.as_deref(),
11035                "Caixa::edicao must byte-equal the raw \
11036                 `self.edicao.as_deref()` field access across every \
11037                 value in the Option<&str> accept-set",
11038            );
11039        }
11040    }
11041
11042    #[test]
11043    fn validate_edicao_empty_arm_routes_through_accessor() {
11044        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
11045        // must key off [`Caixa::edicao`], not the raw
11046        // `self.edicao.as_deref()` field access. Structurally: a
11047        // `Caixa { edicao: Some(""), .. }` must surface the
11048        // `EdicaoEmpty` refusal exactly, and a
11049        // `Caixa { edicao: Some("2026"), .. }` (the canonical
11050        // 4-digit-ASCII-decimal-year form) must pass validate. The
11051        // pair jointly pins the accessor + validate-gate composition:
11052        // any future silent detour that had the accessor return `None`
11053        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
11054        // would silently absorb the `EdicaoEmpty` refusal at the
11055        // accessor boundary and the validate gate would accept a
11056        // struct-literal `Caixa { edicao: Some(""), .. }` — the
11057        // composition pin catches that at caixa-core build time.
11058        //
11059        // Peer of the [`Caixa::licenca`] (6d5bc28)
11060        // `validate_licenca_empty_arm_routes_through_accessor`,
11061        // [`Caixa::repositorio`] (cc7332d)
11062        // `validate_repositorio_empty_arm_routes_through_accessor`,
11063        // and [`Caixa::descricao`] (3f16e2f)
11064        // `validate_descricao_empty_arm_routes_through_accessor`
11065        // composition pins on the sibling outer top-level [`Caixa`]
11066        // `Option<&str>` universal-axis surface — same "the validate /
11067        // shape-gate predicate must route through the substrate-
11068        // primitive typed dispatch" discipline extended onto the
11069        // fourth and final outer top-level [`Caixa`] universal-axis
11070        // `Option<&str>`-composition surface, closing the accessor-
11071        // composition family.
11072        let c = caixa_with_edicao(Some(""));
11073        assert!(
11074            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
11075            "validate_edicao must reject edicao == Some(\"\") with \
11076             EdicaoEmpty — the accessor and the validate gate must \
11077             route through the same substrate-primitive typed dispatch \
11078             on the :edicao empty arm",
11079        );
11080        let c = caixa_with_edicao(Some("2026"));
11081        assert!(
11082            c.validate_edicao().is_ok(),
11083            "validate_edicao must accept edicao == Some(\"2026\") \
11084             (the canonical 4-digit-ASCII-decimal-year shape)",
11085        );
11086    }
11087
11088    #[test]
11089    fn edicao_projects_option_str_by_borrow() {
11090        // The by-borrow pin: [`Caixa::edicao`] returns
11091        // `Option<&str>` by borrow — the `&str` borrows the underlying
11092        // `String` storage of the `Option<String>` slot and the
11093        // accessor must not allocate a fresh `String` on every call.
11094        // Peer of the [`Caixa::licenca`] (6d5bc28),
11095        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
11096        // (3f16e2f) by-borrow pins on the peer outer top-level
11097        // [`Caixa`] `Option<&str>`-return axes, and of the
11098        // per-`:placement`
11099        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11100        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11101        // return axis, extended onto the fourth and final outer top-
11102        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11103        // accessor's returned `&str` must borrow from `&self` (the
11104        // returned reference's lifetime is tied to `&self`), and
11105        // calling the accessor twice on the same [`Caixa`] must yield
11106        // the same `Option<&str>` verbatim (idempotent, no side
11107        // effects on `&self`).
11108        //
11109        // Pins against a future silent detour that returned an owned
11110        // `Option<String>` (which would type-check but silently
11111        // allocate on every call, breaking the zero-cost projection
11112        // every peer sibling accessor carries), or a one-arm-only
11113        // accessor that returned a saturating value on some sentinel
11114        // input (breaking the pass-through invariant the sibling
11115        // required-scalar accessors carry).
11116        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
11117            let c = caixa_with_edicao(edicao);
11118            let first = c.edicao();
11119            let second = c.edicao();
11120            assert_eq!(
11121                first, second,
11122                "Caixa::edicao must be idempotent — two successive \
11123                 calls on the same &self must return the same \
11124                 Option<&str>",
11125            );
11126            assert_eq!(
11127                first, edicao,
11128                "Caixa::edicao must return :edicao verbatim by \
11129                 borrow — got {first:?}, expected {edicao:?}",
11130            );
11131        }
11132    }
11133
11134    #[test]
11135    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
11136        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
11137        // label caixa-identity scalar pin: [`Caixa::nome`] must return
11138        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
11139        // the raw field access across every representative value in
11140        // the accept-set — the canonical `"demo"` template baseline
11141        // (the same `feira init`-scaffolded default the sibling
11142        // `validate_nome_accepts_canonical_template` positive-control
11143        // gate pins), plus every sibling per-typed-slot atom accessor's
11144        // canonical positive-arm byte-string (`"catalog"` per
11145        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
11146        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
11147        // `caixa-helm`/`caixa-flux` cross-crate integration-test
11148        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
11149        // canonical example), plus every past-the-guard sentinel for
11150        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
11151        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
11152        // the bare DNS-1123 63-byte cap but overflows the joint
11153        // `lareira-<nome>` chart-name budget the sibling
11154        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
11155        //
11156        // The past-the-guard sentinels pin the accessor doesn't
11157        // silently absorb the refusal cases into a template-derived
11158        // fallback (a future `.nome().is_empty().then(|| "demo")`
11159        // collapse would silently absorb the `NomeEmpty` refusal at
11160        // the accessor boundary and the validate gate would accept a
11161        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
11162        // catches that at caixa-core build time).
11163        //
11164        // First outer top-level [`Caixa`] `&str`-return required-
11165        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
11166        // required-scalar" projection pattern the sibling per-`Caixa`
11167        // `:versao` future lift folds on. Sibling in shape to the peer
11168        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
11169        // required-`String`-carry accessor pin on the sibling per-
11170        // sub-struct required-axis, extended onto the outer top-level
11171        // [`Caixa`] universal-axis required-`String`-carry axis.
11172        for nome in [
11173            "demo",
11174            "catalog",
11175            "cart",
11176            "hello-rio",
11177            "checkout",
11178            "",
11179            "Bad_Name",
11180            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
11181        ] {
11182            let c = caixa_with_nome(nome);
11183            assert_eq!(
11184                c.nome(),
11185                nome,
11186                "Caixa::nome must return :nome verbatim (got {}, \
11187                 expected {nome})",
11188                c.nome(),
11189            );
11190            assert_eq!(
11191                c.nome(),
11192                c.nome.as_str(),
11193                "Caixa::nome must byte-equal the raw .nome field \
11194                 access across every value in the String accept-set",
11195            );
11196        }
11197    }
11198
11199    #[test]
11200    fn validate_nome_empty_arm_routes_through_accessor() {
11201        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
11202        // key off [`Caixa::nome`], not the raw `.nome` field access.
11203        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
11204        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
11205        // template baseline (the peer positive-arm the sibling
11206        // `validate_nome_accepts_canonical_template` gate carves out)
11207        // must pass validate. The pair jointly pins the accessor +
11208        // validate-gate composition: any future silent detour that
11209        // had the accessor return a fresh `"demo"` on the empty arm
11210        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
11211        // would silently absorb the `NomeEmpty` refusal at the
11212        // accessor boundary and the validate gate would accept a
11213        // struct-literal `Caixa { nome: "".into(), .. }` — the
11214        // composition pin catches that at caixa-core build time.
11215        //
11216        // Peer of the sibling per-`Caixa`
11217        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
11218        // / `validate_repositorio_empty_arm_routes_through_accessor`
11219        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
11220        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
11221        // (2641cbd) composition pins on the sibling outer top-level
11222        // [`Caixa`] `Option<&str>` axes — same "the validate /
11223        // shape-gate predicate must route through the substrate-
11224        // primitive typed dispatch" discipline extended onto the peer
11225        // outer top-level [`Caixa`] required-`&str` composition axis.
11226        let c = caixa_with_nome("");
11227        assert!(
11228            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
11229            "validate_nome must reject nome == \"\" with NomeEmpty — \
11230             the accessor and the validate gate must route through the \
11231             same substrate-primitive typed dispatch on the :nome \
11232             empty-arm",
11233        );
11234        let c = caixa_with_nome("demo");
11235        assert!(
11236            c.validate_nome().is_ok(),
11237            "validate_nome must accept nome == \"demo\" (the canonical \
11238             DNS-1123-label template baseline)",
11239        );
11240    }
11241
11242    #[test]
11243    fn nome_projects_str_by_borrow() {
11244        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
11245        // — the `&str` borrows the underlying `String` storage of the
11246        // required `nome` slot and the accessor must not allocate a
11247        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
11248        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
11249        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
11250        // by-borrow pins on the peer outer top-level [`Caixa`]
11251        // `Option<&str>`-return axes, extended onto the first outer
11252        // top-level [`Caixa`] required-`&str`-return axis — the
11253        // accessor's returned `&str` must borrow from `&self` (the
11254        // returned reference's lifetime is tied to `&self`), and
11255        // calling the accessor twice on the same [`Caixa`] must yield
11256        // the same `&str` verbatim (idempotent, no side effects on
11257        // `&self`).
11258        //
11259        // Pins against a future silent detour that returned an owned
11260        // `String` (which would type-check but silently allocate on
11261        // every call, breaking the zero-cost projection every peer
11262        // sibling accessor carries), an accidental
11263        // `.nome.to_lowercase()` detour that returned a fresh
11264        // allocation through an already-DNS-1123-lowercase-only
11265        // string (breaking a future `const fn` regression), or a
11266        // one-arm-only accessor that returned a canonicalized value
11267        // on some sentinel input (breaking the pass-through invariant
11268        // the sibling required-scalar accessors carry).
11269        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
11270            let c = caixa_with_nome(nome);
11271            let first = c.nome();
11272            let second = c.nome();
11273            assert_eq!(
11274                first, second,
11275                "Caixa::nome must be idempotent — two successive calls \
11276                 on the same &self must return the same &str",
11277            );
11278            assert_eq!(
11279                first, nome,
11280                "Caixa::nome must return :nome verbatim by borrow — \
11281                 got {first}, expected {nome}",
11282            );
11283        }
11284    }
11285
11286    #[test]
11287    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
11288        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
11289        // pinned-version scalar pin: [`Caixa::versao`] must return the
11290        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
11291        // raw `.versao` field access across every representative value
11292        // in the accept-set — the canonical `"0.1.0"` template baseline
11293        // (the same `feira init`-scaffolded default the sibling
11294        // `validate_versao_accepts_canonical_template` positive-control
11295        // gate pins), plus every canonical SemVer-2 shape the sibling
11296        // `validate_versao_accepts_canonical_forms` positive-arm sweep
11297        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
11298        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
11299        // `"10.20.30"`), plus every past-the-guard sentinel for the
11300        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
11301        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
11302        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
11303        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
11304        // `"latest"` the docker-tag-shape footgun — the sentinels pin
11305        // the accessor doesn't silently absorb the refusal cases into a
11306        // template-derived fallback like `"0.1.0"`).
11307        //
11308        // The past-the-guard sentinels pin the accessor doesn't silently
11309        // absorb the refusal cases into a template-derived fallback (a
11310        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
11311        // silently absorb the `VersaoEmpty` refusal at the accessor
11312        // boundary and the validate gate would accept a struct-literal
11313        // `Caixa { versao: "".into(), .. }` — the pin catches that at
11314        // caixa-core build time).
11315        //
11316        // Second outer top-level [`Caixa`] `&str`-return required-scalar
11317        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
11318        // scalar" projection pattern the sibling per-`Caixa`
11319        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
11320        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
11321        // (4127bb6) / per-`:children`
11322        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
11323        // / per-`:upgrade-from`
11324        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
11325        // struct `:versao`-shaped `&str`-return accessor pins on the
11326        // sibling per-typed-slot version-carrier axes, extended onto the
11327        // second outer top-level [`Caixa`] universal-axis required-
11328        // `String`-carry axis so the two universal-axis identity-
11329        // carrying scalars every `defcaixa` form supplies (`:nome` +
11330        // `:versao`) share the same "one typed dispatch per axis" pin
11331        // discipline.
11332        for versao in [
11333            "0.1.0",
11334            "0.0.0",
11335            "1.0.0",
11336            "0.2.0-rc.1",
11337            "1.0.0-alpha.0",
11338            "1.0.0+build.42",
11339            "1.0.0-rc.1+build.42",
11340            "10.20.30",
11341            "",
11342            "v0.1.0",
11343            "0.1",
11344            "^0.1",
11345            "0.1.0.0",
11346            "latest",
11347        ] {
11348            let c = caixa_with_versao(versao);
11349            assert_eq!(
11350                c.versao(),
11351                versao,
11352                "Caixa::versao must return :versao verbatim (got {}, \
11353                 expected {versao})",
11354                c.versao(),
11355            );
11356            assert_eq!(
11357                c.versao(),
11358                c.versao.as_str(),
11359                "Caixa::versao must byte-equal the raw .versao field \
11360                 access across every value in the String accept-set",
11361            );
11362        }
11363    }
11364
11365    #[test]
11366    fn validate_versao_empty_arm_routes_through_accessor() {
11367        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
11368        // must key off [`Caixa::versao`], not the raw `.versao` field
11369        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
11370        // surface the `VersaoEmpty` refusal exactly, and the canonical
11371        // `"0.1.0"` template baseline (the peer positive-arm the sibling
11372        // `validate_versao_accepts_canonical_template` gate carves out)
11373        // must pass validate. The pair jointly pins the accessor +
11374        // validate-gate composition: any future silent detour that had
11375        // the accessor return a fresh `"0.1.0"` on the empty arm
11376        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
11377        // would silently absorb the `VersaoEmpty` refusal at the
11378        // accessor boundary and the validate gate would accept a
11379        // struct-literal `Caixa { versao: "".into(), .. }` — the
11380        // composition pin catches that at caixa-core build time.
11381        //
11382        // Peer of the sibling per-`Caixa`
11383        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
11384        // composition pin on the sibling outer top-level [`Caixa`]
11385        // required-`&str` universal-axis surface — same "the validate /
11386        // shape-gate predicate must route through the substrate-
11387        // primitive typed dispatch" discipline extended onto the peer
11388        // outer top-level [`Caixa`] required-`&str` universal-axis
11389        // pinned-version composition axis, closing the second
11390        // coordinate of the "one canonical typed dispatch per per-Caixa
11391        // required-`&str` universal-axis" discipline.
11392        let c = caixa_with_versao("");
11393        assert!(
11394            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
11395            "validate_versao must reject versao == \"\" with VersaoEmpty — \
11396             the accessor and the validate gate must route through the \
11397             same substrate-primitive typed dispatch on the :versao \
11398             empty-arm",
11399        );
11400        let c = caixa_with_versao("0.1.0");
11401        assert!(
11402            c.validate_versao().is_ok(),
11403            "validate_versao must accept versao == \"0.1.0\" (the \
11404             canonical SemVer-2 template baseline)",
11405        );
11406    }
11407
11408    #[test]
11409    fn versao_projects_str_by_borrow() {
11410        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
11411        // — the `&str` borrows the underlying `String` storage of the
11412        // required `versao` slot and the accessor must not allocate a
11413        // fresh `String` on every call. Peer of the [`Caixa::nome`]
11414        // (e6b7d97) by-borrow pin on the sibling outer top-level
11415        // [`Caixa`] required-`&str`-return axis, extended onto the
11416        // second outer top-level [`Caixa`] required-`&str`-return
11417        // universal-axis pinned-version surface — the accessor's
11418        // returned `&str` must borrow from `&self` (the returned
11419        // reference's lifetime is tied to `&self`), and calling the
11420        // accessor twice on the same [`Caixa`] must yield the same
11421        // `&str` verbatim (idempotent, no side effects on `&self`).
11422        //
11423        // Pins against a future silent detour that returned an owned
11424        // `String` (which would type-check but silently allocate on
11425        // every call, breaking the zero-cost projection every peer
11426        // sibling accessor carries), an accidental
11427        // `semver::Version::parse(&self.versao).unwrap().to_string()`
11428        // detour that returned a canonicalized fresh allocation through
11429        // an already-canonical byte-string (breaking a future `const fn`
11430        // regression and silently absorbing the `VersaoInvalid` refusal
11431        // at the accessor boundary), or a one-arm-only accessor that
11432        // returned a canonicalized value on some sentinel input
11433        // (breaking the pass-through invariant the sibling required-
11434        // scalar accessors carry).
11435        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
11436            let c = caixa_with_versao(versao);
11437            let first = c.versao();
11438            let second = c.versao();
11439            assert_eq!(
11440                first, second,
11441                "Caixa::versao must be idempotent — two successive \
11442                 calls on the same &self must return the same &str",
11443            );
11444            assert_eq!(
11445                first, versao,
11446                "Caixa::versao must return :versao verbatim by borrow \
11447                 — got {first}, expected {versao}",
11448            );
11449        }
11450    }
11451
11452    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
11453        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11454        c.kind = kind;
11455        c
11456    }
11457
11458    #[test]
11459    fn kind_returns_kind_variant_verbatim_across_permutations() {
11460        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
11461        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
11462        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
11463        // the raw `.kind` field access across every variant in the
11464        // closed accept-set (`Biblioteca` — the library kind that
11465        // exports lisp forms; `Binario` — the nix-built executable kind
11466        // under `exe/`; `Servico` — the wasm-component daemon kind
11467        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
11468        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
11469        // composition kind).
11470        //
11471        // Pins against a future silent detour that re-derived the kind
11472        // from a peer axis (an accidental fallback to
11473        // `if !servicos.is_empty() { Servico } else if
11474        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
11475        // collapse that read the code-surface / mesh-slot columns into
11476        // the kind discriminator), a variant remap the operator
11477        // authors on one consumer without the other, or a stale-derive
11478        // detour that substituted [`CaixaKind::Biblioteca`] as the
11479        // default when the field held any other variant (which would
11480        // silently collapse the distinction between "author explicitly
11481        // declared `:kind Servico`" and "author declared any other
11482        // kind" every downstream renderer-dispatch site depends on).
11483        //
11484        // First outer top-level [`Caixa`] `Copy`-return required-enum-
11485        // discriminant accessor pin — opens the "outer [`Caixa`]
11486        // `Copy`-return required-discriminant" projection pattern.
11487        // Sibling in shape to the peer per-`:supervisor`
11488        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
11489        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
11490        // (921fe1b), and per-`:children`
11491        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
11492        // `Copy`-return closed-set-enum discriminant accessor pins on
11493        // the sibling nested-spec typed-slot discriminator axes,
11494        // extended here to the outer top-level [`Caixa`] universal-
11495        // axis surface.
11496        for kind in [
11497            CaixaKind::Biblioteca,
11498            CaixaKind::Binario,
11499            CaixaKind::Servico,
11500            CaixaKind::Supervisor,
11501            CaixaKind::Aplicacao,
11502        ] {
11503            let c = caixa_with_kind(kind);
11504            assert_eq!(
11505                c.kind(),
11506                kind,
11507                "Caixa::kind must return :kind verbatim (got {:?}, \
11508                 expected {kind:?})",
11509                c.kind(),
11510            );
11511            assert_eq!(
11512                c.kind(),
11513                c.kind,
11514                "Caixa::kind accessor and .kind field access must \
11515                 byte-equal — the accessor is the substrate-primitive \
11516                 typed dispatch every downstream kind-gate consumer \
11517                 must route through",
11518            );
11519        }
11520    }
11521
11522    #[test]
11523    fn require_kind_reads_through_lifted_kind_accessor() {
11524        // Two-consumer coherence pin: the [`crate::render::require_kind`]
11525        // entry-gate predicate (the canonical two-line
11526        // `require_kind(caixa, Servico)?` prelude every per-Servico /
11527        // per-Aplicacao renderer runs at its entry-point) and the
11528        // sibling [`crate::render::KindMismatch`] error carrier's
11529        // `actual:` field (which names the offending caixa's variant
11530        // in the diagnostic) must both key off the lifted accessor, so
11531        // any future rebrand on the typed slot's reader shape lands at
11532        // exactly one place. Pins the two-site coherence by exercising
11533        // every off-diagonal `(actual, expected)` pair across the
11534        // closed accept-set — the `KindMismatch { actual, expected }`
11535        // surfaced on the mismatch arm must byte-equal the pair the
11536        // accessor returns for each side.
11537        //
11538        // Peer of the sibling per-`:placement`
11539        // `validate_placement_reads_through_lifted_estrategia_accessor`
11540        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
11541        // `Copy`-return discriminant axis — same "the entry-gate
11542        // predicate and the error carrier's `actual:` field must route
11543        // through the substrate-primitive typed dispatch" discipline
11544        // extended onto the outer top-level [`Caixa`] universal-axis
11545        // discriminant surface.
11546        for expected in [
11547            CaixaKind::Biblioteca,
11548            CaixaKind::Binario,
11549            CaixaKind::Servico,
11550            CaixaKind::Supervisor,
11551            CaixaKind::Aplicacao,
11552        ] {
11553            for actual in [
11554                CaixaKind::Biblioteca,
11555                CaixaKind::Binario,
11556                CaixaKind::Servico,
11557                CaixaKind::Supervisor,
11558                CaixaKind::Aplicacao,
11559            ] {
11560                let c = caixa_with_kind(actual);
11561                let result = crate::render::require_kind(&c, expected);
11562                if expected == actual {
11563                    assert!(
11564                        result.is_ok(),
11565                        "require_kind must accept when actual == expected \
11566                         (actual={actual:?}, expected={expected:?})",
11567                    );
11568                } else {
11569                    let err = result.expect_err("require_kind must reject when actual != expected");
11570                    assert_eq!(
11571                        err.actual,
11572                        c.kind(),
11573                        "KindMismatch.actual must byte-equal Caixa::kind() \
11574                         — the error carrier's `actual:` field reads \
11575                         through the lifted accessor",
11576                    );
11577                    assert_eq!(
11578                        err.expected, expected,
11579                        "KindMismatch.expected must byte-equal the \
11580                         expected variant passed to require_kind",
11581                    );
11582                }
11583            }
11584        }
11585    }
11586
11587    #[test]
11588    fn aplicacao_view_kind_gate_routes_through_accessor() {
11589        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
11590        // must key off [`Caixa::kind`], not the raw `.kind` field
11591        // access. Structurally: a `Caixa { kind: X, .. }` for any
11592        // non-`Aplicacao` variant must fold to `None` on the
11593        // `aplicacao_view` composer (the "kind mismatch → no typed
11594        // view" contract every downstream Aplicacao consumer keys off
11595        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
11596        // `Some(_)`. The pair jointly pins the accessor + view-gate
11597        // composition: any future silent detour that had the accessor
11598        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
11599        // input would silently absorb the kind-mismatch case at the
11600        // accessor boundary and every per-Aplicacao renderer would
11601        // silently render a non-Aplicacao caixa's mesh slots — the
11602        // composition pin catches that at caixa-core build time.
11603        //
11604        // Peer of the sibling per-`Caixa`
11605        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
11606        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
11607        // composition pins on the sibling outer top-level [`Caixa`]
11608        // required-`&str` universal-axis surfaces — same "the
11609        // composer / validate gate must route through the substrate-
11610        // primitive typed dispatch" discipline extended onto the
11611        // outer top-level [`Caixa`] `Copy`-return required-
11612        // discriminant composition axis.
11613        for kind in [
11614            CaixaKind::Biblioteca,
11615            CaixaKind::Binario,
11616            CaixaKind::Servico,
11617            CaixaKind::Supervisor,
11618        ] {
11619            let c = caixa_with_kind(kind);
11620            assert!(
11621                c.aplicacao_view().is_none(),
11622                "aplicacao_view must return None on non-Aplicacao \
11623                 kind {kind:?} — the composer's kind-gate must route \
11624                 through Caixa::kind()",
11625            );
11626        }
11627        let c = caixa_with_kind(CaixaKind::Aplicacao);
11628        assert!(
11629            c.aplicacao_view().is_some(),
11630            "aplicacao_view must return Some on kind Aplicacao — \
11631             the composer's kind-gate must accept the matching arm \
11632             through Caixa::kind()",
11633        );
11634    }
11635
11636    #[test]
11637    fn supervisor_view_kind_gate_routes_through_accessor() {
11638        // Composition pin (mirror of the sibling
11639        // `aplicacao_view_kind_gate_routes_through_accessor` on the
11640        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
11641        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
11642        // field access. A `Caixa { kind: X, .. }` for any non-
11643        // `Supervisor` variant must fold to `None` on the
11644        // `supervisor_view` composer, and a `Caixa { kind:
11645        // Supervisor, .. }` must fold to `Some(_)`. Same peer
11646        // composition pin discipline on the second `_view` composer
11647        // axis.
11648        for kind in [
11649            CaixaKind::Biblioteca,
11650            CaixaKind::Binario,
11651            CaixaKind::Servico,
11652            CaixaKind::Aplicacao,
11653        ] {
11654            let c = caixa_with_kind(kind);
11655            assert!(
11656                c.supervisor_view().is_none(),
11657                "supervisor_view must return None on non-Supervisor \
11658                 kind {kind:?} — the composer's kind-gate must route \
11659                 through Caixa::kind()",
11660            );
11661        }
11662        let mut c = caixa_with_kind(CaixaKind::Supervisor);
11663        // A Supervisor caixa needs a strategy + at least one child to
11664        // fold to a Some(_) that also validates; the composer itself
11665        // requires only the kind arm, so bare kind flip is enough to
11666        // pin the `Some(_)` return, but we populate the minimum
11667        // supervisor shape so a future strengthening of the composer
11668        // to reject an empty spec doesn't false-positive this pin.
11669        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
11670        c.children = vec![crate::supervisor::ChildSpec {
11671            caixa: "child".into(),
11672            versao: "^0.1".into(),
11673            restart: crate::supervisor::RestartPolicy::Permanent,
11674        }];
11675        assert!(
11676            c.supervisor_view().is_some(),
11677            "supervisor_view must return Some on kind Supervisor — \
11678             the composer's kind-gate must accept the matching arm \
11679             through Caixa::kind()",
11680        );
11681    }
11682
11683    #[test]
11684    fn kind_projects_by_copy() {
11685        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
11686        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
11687        // `&self` (the returned value is owned, `Copy`-projected from
11688        // the underlying [`CaixaKind`] storage; two calls on the same
11689        // [`Caixa`] must yield byte-equal values). Peer of the peer
11690        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
11691        // `SupervisorSpec::estrategia` / per-`:children`
11692        // `ChildSpec::restart` `Copy`-return discriminant accessor
11693        // pins on the sibling nested-spec typed-slot discriminator
11694        // axes, extended onto the first outer top-level [`Caixa`]
11695        // required-`Copy`-return axis — pins against a future silent
11696        // detour that returned `&CaixaKind` (which would type-check
11697        // but silently constrain every consumer's callsite to a
11698        // borrow-shaped dispatch, breaking the zero-cost `Copy`
11699        // projection every peer sibling accessor carries).
11700        for kind in [
11701            CaixaKind::Biblioteca,
11702            CaixaKind::Binario,
11703            CaixaKind::Servico,
11704            CaixaKind::Supervisor,
11705            CaixaKind::Aplicacao,
11706        ] {
11707            let c = caixa_with_kind(kind);
11708            let first: CaixaKind = c.kind();
11709            let second: CaixaKind = c.kind();
11710            assert_eq!(
11711                first, second,
11712                "Caixa::kind must be idempotent — two successive \
11713                 calls on the same &self must return the same \
11714                 CaixaKind variant",
11715            );
11716            assert_eq!(
11717                first, kind,
11718                "Caixa::kind must return :kind verbatim by Copy — \
11719                 got {first:?}, expected {kind:?}",
11720            );
11721        }
11722    }
11723
11724    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
11725
11726    #[test]
11727    fn autores_returns_autores_slice_verbatim_across_permutations() {
11728        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
11729        // name-list slice pin: [`Caixa::autores`] must return the
11730        // `:autores` typed [`Vec<String>`] list verbatim as a
11731        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
11732        // access across every representative value in the accept-set —
11733        // `[]` (the "no maintainers declared" arm every existing
11734        // fixture without an `:autores` line carries), `[""]` (a past-
11735        // the-guard sentinel that pins the accessor doesn't perform a
11736        // silent `[""] → []` collapse on the empty-entry arm — validate
11737        // rejects `[""]` through `AutorEmpty` but the accessor must
11738        // ship the raw slot verbatim so a validate-time gate regression
11739        // surfaces at the caixa-helm emit boundary rather than being
11740        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
11741        // canonical single-maintainer form every `feira init` template
11742        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
11743        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
11744        // (the canonical RFC-5322 `<name> <email>` form the
11745        // `is_chart_maintainer_name_shape` predicate accepts), and
11746        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
11747        // sentinel — validate rejects through `AutorDuplicate` but the
11748        // accessor must ship the raw slot verbatim).
11749        //
11750        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
11751        // pin on the substrate primitive — opens the "outer [`Caixa`]
11752        // `&[T]` slice" projection pattern the sibling per-`Caixa`
11753        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
11754        // / `:servicos` / `:upgrade-from` / `:children` future lifts
11755        // fold on. Sibling in shape to the peer per-`:supervisor`
11756        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
11757        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
11758        // (a6e18d7), per-`:membros`
11759        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
11760        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11761        // (0dcc926), and per-`:upgrade-from :instructions`
11762        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
11763        // `&[T]`-return slice accessor pins on the sibling per-M2 /
11764        // per-M3 typed-slot list axes, extended onto the outer top-
11765        // level [`Caixa`] universal-axis surface. Pins against a future
11766        // silent detour that returned an owned `Vec<String>` (which
11767        // would type-check but silently clone on every accessor call,
11768        // breaking the zero-cost projection every peer sibling slice
11769        // accessor carries), a `[""] → []` collapse (which would
11770        // silently absorb the `AutorEmpty` refusal case at the accessor
11771        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
11772        // would silently absorb the `AutorDuplicate` refusal case at
11773        // the accessor boundary and the caixa-helm `maintainers:` fold
11774        // would silently render a dedupped list on a struct-literal
11775        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
11776        for autores in [
11777            vec![],
11778            vec![""],
11779            vec!["pleme-io"],
11780            vec!["alice", "bob"],
11781            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
11782            vec!["pleme-io", "pleme-io"],
11783        ] {
11784            let c = caixa_with_autores(autores.clone());
11785            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11786            assert_eq!(
11787                c.autores(),
11788                expected.as_slice(),
11789                "Caixa::autores must return :autores verbatim (got {:?}, \
11790                 expected {expected:?})",
11791                c.autores(),
11792            );
11793            assert_eq!(
11794                c.autores(),
11795                c.autores.as_slice(),
11796                "Caixa::autores must byte-equal the raw \
11797                 `self.autores.as_slice()` field access across every \
11798                 value in the Vec<String> accept-set",
11799            );
11800        }
11801    }
11802
11803    #[test]
11804    fn validate_autores_empty_entry_arm_routes_through_accessor() {
11805        // Composition pin: [`Caixa::validate_autores`]'s per-entry
11806        // empty-arm gate must key off [`Caixa::autores`], not the raw
11807        // `&self.autores` field-borrow walk. Structurally: a
11808        // `Caixa { autores: vec!["".into()], .. }` must surface the
11809        // `AutorEmpty` refusal exactly, and a
11810        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
11811        // canonical single-maintainer form) must pass validate. The
11812        // pair jointly pins the accessor + validate-gate composition:
11813        // any future silent detour that had the accessor return an
11814        // empty slice on the `[""]` arm (a
11815        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
11816        // would silently absorb the `AutorEmpty` refusal at the
11817        // accessor boundary and the validate gate would accept a
11818        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
11819        // the composition pin catches that at caixa-core build time.
11820        //
11821        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
11822        // accessor-composition pin
11823        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
11824        // sibling `Option<&str>`-composition axis and the
11825        // per-`:politicas :circuit-breaker`
11826        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11827        // accessor-composition pin
11828        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11829        // on the sibling required-`u32`-composition axis — same "the
11830        // validate / shape-gate predicate must route through the
11831        // substrate-primitive typed dispatch" discipline extended onto
11832        // the outer top-level [`Caixa`] universal-axis `&[T]`-
11833        // composition surface.
11834        let c = caixa_with_autores(vec![""]);
11835        assert!(
11836            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
11837            "validate_autores must reject autores == vec![\"\"] with \
11838             AutorEmpty — the accessor and the validate gate must \
11839             route through the same substrate-primitive typed dispatch \
11840             on the :autores per-entry empty arm",
11841        );
11842        let c = caixa_with_autores(vec!["pleme-io"]);
11843        assert!(
11844            c.validate_autores().is_ok(),
11845            "validate_autores must accept autores == vec![\"pleme-io\"] \
11846             (the canonical single-maintainer shape every `feira init` \
11847             template scaffolds)",
11848        );
11849    }
11850
11851    #[test]
11852    fn autores_projects_slice_by_borrow() {
11853        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
11854        // borrow — the returned slice borrows the underlying
11855        // `Vec<String>` storage of the `:autores` slot and the
11856        // accessor must not clone the backing `Vec` on every call.
11857        // Peer of the per-`:membros`
11858        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
11859        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
11860        // (0dcc926) / per-`:placement`
11861        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
11862        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
11863        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
11864        // typed-slot `&[T]`-return axes, extended onto the outer top-
11865        // level [`Caixa`] universal-axis `&[String]` shape — the
11866        // accessor's returned slice must borrow from `&self` (the
11867        // returned reference's lifetime is tied to `&self`), and
11868        // calling the accessor twice on the same [`Caixa`] must yield
11869        // slices that are pointer-equal (the underlying byte-buffer is
11870        // the storage `Vec`'s allocation, not a fresh copy) as well as
11871        // value-equal (idempotent, no side effects on `&self`).
11872        //
11873        // Pins against a future silent detour that returned an owned
11874        // `Vec<String>` (which would type-check but silently clone on
11875        // every call, breaking the zero-cost projection every peer
11876        // sibling slice accessor carries), a `&Vec<String>` return
11877        // (which would leak the backing `Vec`'s grow/push/reserve
11878        // surface no downstream consumer reaches for), or a one-arm-
11879        // only accessor that returned a saturating value on some
11880        // sentinel input (breaking the pass-through invariant the
11881        // sibling slice accessors carry).
11882        for autores in [
11883            vec![],
11884            vec!["pleme-io"],
11885            vec!["alice", "bob"],
11886            vec!["pleme-io", "pleme-io"],
11887        ] {
11888            let c = caixa_with_autores(autores.clone());
11889            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
11890            let first = c.autores();
11891            let second = c.autores();
11892            assert_eq!(
11893                first, second,
11894                "Caixa::autores must be idempotent — two successive \
11895                 calls on the same &self must return the same \
11896                 &[String]",
11897            );
11898            assert_eq!(
11899                first.as_ptr(),
11900                second.as_ptr(),
11901                "Caixa::autores must borrow the underlying Vec<String> \
11902                 storage — two successive calls must return slices \
11903                 with the same backing pointer (a fresh Vec<String> \
11904                 clone would change the pointer on every call)",
11905            );
11906            assert_eq!(
11907                first,
11908                expected.as_slice(),
11909                "Caixa::autores must return :autores verbatim by \
11910                 borrow — got {first:?}, expected {expected:?}",
11911            );
11912        }
11913    }
11914
11915    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
11916
11917    #[test]
11918    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
11919        // The canonical per-`Caixa` `:etiquetas` universal-axis
11920        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
11921        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
11922        // as a `&[String]`, byte-equal to the raw
11923        // `self.etiquetas.as_slice()` access across every representative
11924        // value in the accept-set — `[]` (the "no tags declared" arm
11925        // every existing fixture without an `:etiquetas` line carries),
11926        // `[""]` (a past-the-guard sentinel that pins the accessor
11927        // doesn't perform a silent `[""] → []` collapse on the empty-
11928        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
11929        // but the accessor must ship the raw slot verbatim so a
11930        // validate-time gate regression surfaces at the caixa-helm emit
11931        // boundary rather than being silently absorbed into a keyword-
11932        // drop), `["demo"]` (the canonical single-tag form every
11933        // `feira init` template scaffolds), `["example", "aplicacao",
11934        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
11935        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
11936        // (a past-the-guard duplicate sentinel — validate rejects
11937        // through `EtiquetaDuplicate` but the accessor must ship the
11938        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
11939        // at chart-render time isn't silently promoted into the
11940        // accessor boundary and struct-literal
11941        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
11942        // fixtures continue to expose the duplicate at the accessor).
11943        //
11944        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
11945        // pin on the substrate primitive — folds on the "outer
11946        // [`Caixa`] `&[T]` slice" projection pattern
11947        // `autores_returns_autores_slice_verbatim_across_permutations`
11948        // (b5d813f) opened, sibling in shape and idiom. Pins against a
11949        // future silent detour that returned an owned `Vec<String>`
11950        // (which would type-check but silently clone on every accessor
11951        // call, breaking the zero-cost projection every peer sibling
11952        // slice accessor carries), a `[""] → []` collapse (which would
11953        // silently absorb the `EtiquetaEmpty` refusal case at the
11954        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
11955        // (which would silently absorb the `EtiquetaDuplicate` refusal
11956        // case at the accessor boundary — the caixa-helm chart-render
11957        // `BTreeSet::collect` dedup is downstream of the accessor and
11958        // must not be silently promoted into it).
11959        for etiquetas in [
11960            vec![],
11961            vec![""],
11962            vec!["demo"],
11963            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
11964            vec!["demo", "demo"],
11965        ] {
11966            let c = caixa_with_etiquetas(etiquetas.clone());
11967            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
11968            assert_eq!(
11969                c.etiquetas(),
11970                expected.as_slice(),
11971                "Caixa::etiquetas must return :etiquetas verbatim (got \
11972                 {:?}, expected {expected:?})",
11973                c.etiquetas(),
11974            );
11975            assert_eq!(
11976                c.etiquetas(),
11977                c.etiquetas.as_slice(),
11978                "Caixa::etiquetas must byte-equal the raw \
11979                 `self.etiquetas.as_slice()` field access across every \
11980                 value in the Vec<String> accept-set",
11981            );
11982        }
11983    }
11984
11985    #[test]
11986    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
11987        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
11988        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
11989        // `&self.etiquetas` field-borrow walk. Structurally: a
11990        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
11991        // `EtiquetaEmpty` refusal exactly, and a
11992        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
11993        // single-tag form) must pass validate. The pair jointly pins
11994        // the accessor + validate-gate composition: any future silent
11995        // detour that had the accessor return an empty slice on the
11996        // `[""]` arm (a
11997        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
11998        // silently absorb the `EtiquetaEmpty` refusal at the accessor
11999        // boundary and the validate gate would accept a struct-literal
12000        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
12001        // pin catches that at caixa-core build time.
12002        //
12003        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12004        // through_accessor` (b5d813f) accessor-composition pin on the
12005        // sibling `&[T]`-composition axis — same "the validate / shape-
12006        // gate predicate must route through the substrate-primitive
12007        // typed dispatch" discipline extended onto the sibling outer
12008        // top-level [`Caixa`] `&[T]`-composition surface.
12009        let c = caixa_with_etiquetas(vec![""]);
12010        assert!(
12011            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
12012            "validate_etiquetas must reject etiquetas == vec![\"\"] \
12013             with EtiquetaEmpty — the accessor and the validate gate \
12014             must route through the same substrate-primitive typed \
12015             dispatch on the :etiquetas per-entry empty arm",
12016        );
12017        let c = caixa_with_etiquetas(vec!["demo"]);
12018        assert!(
12019            c.validate_etiquetas().is_ok(),
12020            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
12021             (the canonical single-tag shape every `feira init` \
12022             template scaffolds)",
12023        );
12024    }
12025
12026    #[test]
12027    fn etiquetas_projects_slice_by_borrow() {
12028        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
12029        // by borrow — the returned slice borrows the underlying
12030        // `Vec<String>` storage of the `:etiquetas` slot and the
12031        // accessor must not clone the backing `Vec` on every call.
12032        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12033        // (b5d813f) by-borrow pin on the sibling outer top-level
12034        // [`Caixa`] `&[String]`-return axis — the accessor's returned
12035        // slice must borrow from `&self` (the returned reference's
12036        // lifetime is tied to `&self`), and calling the accessor twice
12037        // on the same [`Caixa`] must yield slices that are pointer-
12038        // equal (the underlying byte-buffer is the storage `Vec`'s
12039        // allocation, not a fresh copy) as well as value-equal
12040        // (idempotent, no side effects on `&self`).
12041        //
12042        // Pins against a future silent detour that returned an owned
12043        // `Vec<String>` (which would type-check but silently clone on
12044        // every call, breaking the zero-cost projection every peer
12045        // sibling slice accessor carries), a `&Vec<String>` return
12046        // (which would leak the backing `Vec`'s grow/push/reserve
12047        // surface no downstream consumer reaches for), or a one-arm-
12048        // only accessor that returned a saturating value on some
12049        // sentinel input (breaking the pass-through invariant the
12050        // sibling slice accessors carry).
12051        for etiquetas in [
12052            vec![],
12053            vec!["demo"],
12054            vec!["example", "aplicacao", "mesh"],
12055            vec!["demo", "demo"],
12056        ] {
12057            let c = caixa_with_etiquetas(etiquetas.clone());
12058            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
12059            let first = c.etiquetas();
12060            let second = c.etiquetas();
12061            assert_eq!(
12062                first, second,
12063                "Caixa::etiquetas must be idempotent — two successive \
12064                 calls on the same &self must return the same \
12065                 &[String]",
12066            );
12067            assert_eq!(
12068                first.as_ptr(),
12069                second.as_ptr(),
12070                "Caixa::etiquetas must borrow the underlying \
12071                 Vec<String> storage — two successive calls must \
12072                 return slices with the same backing pointer (a fresh \
12073                 Vec<String> clone would change the pointer on every \
12074                 call)",
12075            );
12076            assert_eq!(
12077                first,
12078                expected.as_slice(),
12079                "Caixa::etiquetas must return :etiquetas verbatim by \
12080                 borrow — got {first:?}, expected {expected:?}",
12081            );
12082        }
12083    }
12084
12085    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
12086
12087    #[test]
12088    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
12089        // The canonical per-`Caixa` `:bibliotecas` universal-axis
12090        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
12091        // must return the `:bibliotecas` typed [`Vec<String>`] list
12092        // verbatim as a `&[String]`, byte-equal to the raw
12093        // `self.bibliotecas.as_slice()` access across every
12094        // representative value in the accept-set — `[]` (the "no
12095        // libraries declared" arm every `:kind` other than `Biblioteca`
12096        // + every `Biblioteca` relying on the canonical
12097        // `lib/<nome>.lisp` implicit-default path carries; the
12098        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
12099        // fires exactly on this empty-slot + `Biblioteca`-kind
12100        // combination), `[""]` (a past-the-guard sentinel that pins
12101        // the accessor doesn't perform a silent `[""] → []` collapse
12102        // on the empty-entry arm — validate rejects `[""]` through
12103        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
12104        // must ship the raw slot verbatim so a validate-time gate
12105        // regression surfaces at the `feira build` phase-1 parse
12106        // boundary rather than being silently absorbed into a
12107        // library-drop), `["lib/demo.lisp"]` (the canonical single-
12108        // entry form `Caixa::template` scaffolds and every `feira init`
12109        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
12110        // (the canonical multi-library form the
12111        // `validate_code_paths_accepts_explicit_relative_paths_on_
12112        // every_slot` fixture emits), and `["lib/foo.lisp",
12113        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
12114        // validate rejects through `CodePathDuplicate { slot:
12115        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
12116        // but the accessor must ship the raw slot verbatim so the
12117        // `feira build` `for entry in caixa.bibliotecas()` parse walk
12118        // sees the duplicate at the accessor boundary and struct-
12119        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
12120        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
12121        // the duplicate at the accessor).
12122        //
12123        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
12124        // pin on the substrate primitive — folds on the "outer
12125        // [`Caixa`] `&[T]` slice" projection pattern
12126        // `autores_returns_autores_slice_verbatim_across_permutations`
12127        // (b5d813f) opened and
12128        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12129        // (78c7d3c) folded on, sibling in shape and idiom. Pins
12130        // against a future silent detour that returned an owned
12131        // `Vec<String>` (which would type-check but silently clone on
12132        // every accessor call, breaking the zero-cost projection
12133        // every peer sibling slice accessor carries), a `[""] → []`
12134        // collapse (which would silently absorb the `CodePathEmpty`
12135        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
12136        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
12137        // would silently absorb the `CodePathDuplicate` refusal case
12138        // at the accessor boundary — the per-slot set-not-multiset
12139        // gate is downstream of the accessor and must not be silently
12140        // promoted into it).
12141        for bibliotecas in [
12142            vec![],
12143            vec![""],
12144            vec!["lib/demo.lisp"],
12145            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12146            vec!["lib/foo.lisp", "lib/foo.lisp"],
12147        ] {
12148            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12149            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12150            assert_eq!(
12151                c.bibliotecas(),
12152                expected.as_slice(),
12153                "Caixa::bibliotecas must return :bibliotecas verbatim \
12154                 (got {:?}, expected {expected:?})",
12155                c.bibliotecas(),
12156            );
12157            assert_eq!(
12158                c.bibliotecas(),
12159                c.bibliotecas.as_slice(),
12160                "Caixa::bibliotecas must byte-equal the raw \
12161                 `self.bibliotecas.as_slice()` field access across \
12162                 every value in the Vec<String> accept-set",
12163            );
12164        }
12165    }
12166
12167    #[test]
12168    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
12169        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12170        // empty-arm gate on the `:bibliotecas` slot must key off
12171        // [`Caixa::bibliotecas`], not a divergent raw
12172        // `&self.bibliotecas` field-borrow walk. Structurally: a
12173        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
12174        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
12175        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
12176        // into()], .. }` (the canonical single-library form
12177        // `Caixa::template` scaffolds) must pass validate. The pair
12178        // jointly pins the accessor + validate-gate composition: any
12179        // future silent detour that had the accessor return an empty
12180        // slice on the `[""]` arm (a `.iter().filter(|s|
12181        // !s.is_empty()).collect()` collapse) would silently absorb
12182        // the `CodePathEmpty` refusal at the accessor boundary and
12183        // the validate gate would accept a struct-literal
12184        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
12185        // composition pin catches that at caixa-core build time.
12186        //
12187        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
12188        // through_accessor` (b5d813f) and
12189        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12190        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12191        // composition axes — same "the validate / shape-gate
12192        // predicate must route through the substrate-primitive typed
12193        // dispatch" discipline extended onto the sibling outer top-
12194        // level [`Caixa`] `&[T]`-composition surface. Nominally the
12195        // in-tree `validate_code_paths` production body still keys
12196        // off the internal `[(":bibliotecas", &self.bibliotecas,
12197        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12198        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12199        // (the tuple's homogeneous slice-typed shape blocks a per-
12200        // element accessor swap in isolation — a future companion
12201        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
12202        // `&[T]` slice-accessor axis closes that tuple onto the
12203        // triple of typed dispatches as a unit); the composition pin
12204        // catches any future accessor-side silent filter drop against
12205        // that eventual tuple-closure regardless of whether the
12206        // `:bibliotecas` slot is threaded through the accessor or the
12207        // raw field access at the tuple's construction site.
12208        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
12209        assert!(
12210            matches!(
12211                c.validate_code_paths(),
12212                Err(ManifestError::CodePathEmpty {
12213                    slot: ":bibliotecas"
12214                })
12215            ),
12216            "validate_code_paths must reject bibliotecas == vec![\"\"] \
12217             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
12218             accessor and the validate gate must route through the \
12219             same substrate-primitive typed dispatch on the \
12220             :bibliotecas per-entry empty arm",
12221        );
12222        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
12223        assert!(
12224            c.validate_code_paths().is_ok(),
12225            "validate_code_paths must accept bibliotecas == \
12226             vec![\"lib/demo.lisp\"] (the canonical single-library \
12227             shape every `feira init` template scaffolds)",
12228        );
12229    }
12230
12231    #[test]
12232    fn bibliotecas_projects_slice_by_borrow() {
12233        // The by-borrow pin: [`Caixa::bibliotecas`] returns
12234        // `&[String]` by borrow — the returned slice borrows the
12235        // underlying `Vec<String>` storage of the `:bibliotecas` slot
12236        // and the accessor must not clone the backing `Vec` on every
12237        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12238        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
12239        // by-borrow pins on the sibling outer top-level [`Caixa`]
12240        // `&[String]`-return axes — the accessor's returned slice
12241        // must borrow from `&self` (the returned reference's lifetime
12242        // is tied to `&self`), and calling the accessor twice on the
12243        // same [`Caixa`] must yield slices that are pointer-equal
12244        // (the underlying byte-buffer is the storage `Vec`'s
12245        // allocation, not a fresh copy) as well as value-equal
12246        // (idempotent, no side effects on `&self`).
12247        //
12248        // Pins against a future silent detour that returned an owned
12249        // `Vec<String>` (which would type-check but silently clone on
12250        // every call, breaking the zero-cost projection every peer
12251        // sibling slice accessor carries), a `&Vec<String>` return
12252        // (which would leak the backing `Vec`'s grow/push/reserve
12253        // surface no downstream consumer reaches for), or a one-arm-
12254        // only accessor that returned a saturating value on some
12255        // sentinel input (breaking the pass-through invariant the
12256        // sibling slice accessors carry).
12257        for bibliotecas in [
12258            vec![],
12259            vec!["lib/demo.lisp"],
12260            vec!["lib/demo.lisp", "lib/helpers.lisp"],
12261            vec!["lib/foo.lisp", "lib/foo.lisp"],
12262        ] {
12263            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
12264            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
12265            let first = c.bibliotecas();
12266            let second = c.bibliotecas();
12267            assert_eq!(
12268                first, second,
12269                "Caixa::bibliotecas must be idempotent — two \
12270                 successive calls on the same &self must return the \
12271                 same &[String]",
12272            );
12273            assert_eq!(
12274                first.as_ptr(),
12275                second.as_ptr(),
12276                "Caixa::bibliotecas must borrow the underlying \
12277                 Vec<String> storage — two successive calls must \
12278                 return slices with the same backing pointer (a \
12279                 fresh Vec<String> clone would change the pointer on \
12280                 every call)",
12281            );
12282            assert_eq!(
12283                first,
12284                expected.as_slice(),
12285                "Caixa::bibliotecas must return :bibliotecas verbatim \
12286                 by borrow — got {first:?}, expected {expected:?}",
12287            );
12288        }
12289    }
12290
12291    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
12292
12293    #[test]
12294    fn exe_returns_exe_slice_verbatim_across_permutations() {
12295        // The canonical per-`Caixa` `:exe` universal-axis
12296        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
12297        // must return the `:exe` typed [`Vec<String>`] list verbatim as
12298        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
12299        // access across every representative value in the accept-set —
12300        // `[]` (the "no executable declared" arm every `:kind` other
12301        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
12302        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
12303        // + `Binario`-kind combination), `[""]` (a past-the-guard
12304        // sentinel that pins the accessor doesn't perform a silent
12305        // `[""] → []` collapse on the empty-entry arm — validate rejects
12306        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
12307        // accessor must ship the raw slot verbatim so a validate-time
12308        // gate regression surfaces at the layout / `feira nix` boundary
12309        // rather than being silently absorbed into an executable-drop),
12310        // `["exe/cli"]` (the canonical single-entry Binario form every
12311        // in-tree `caixa_with_code_paths` positive control uses),
12312        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
12313        // form the `validate_code_paths_accepts_explicit_relative_paths_
12314        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
12315        // (a past-the-guard duplicate sentinel — validate rejects
12316        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
12317        // set-not-multiset gate, but the accessor must ship the raw
12318        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
12319        // into(), "exe/cli".into()], .. }` fixtures continue to expose
12320        // the duplicate at the accessor).
12321        //
12322        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
12323        // pin on the substrate primitive — folds on the "outer
12324        // [`Caixa`] `&[T]` slice" projection pattern
12325        // `autores_returns_autores_slice_verbatim_across_permutations`
12326        // (b5d813f) opened,
12327        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12328        // (78c7d3c) folded on, and
12329        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12330        // (8a36c23) closed the universal-axis text-tag family of.
12331        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
12332        // the sibling `:servicos` future lift closes onto. Pins against
12333        // a future silent detour that returned an owned `Vec<String>`
12334        // (which would type-check but silently clone on every accessor
12335        // call, breaking the zero-cost projection every peer sibling
12336        // slice accessor carries), a `[""] → []` collapse (which would
12337        // silently absorb the `CodePathEmpty` refusal case at the
12338        // accessor boundary), or an `["exe/cli", "exe/cli"] →
12339        // ["exe/cli"]` dedup collapse (which would silently absorb the
12340        // `CodePathDuplicate` refusal case at the accessor boundary —
12341        // the per-slot set-not-multiset gate is downstream of the
12342        // accessor and must not be silently promoted into it).
12343        for exe in [
12344            vec![],
12345            vec![""],
12346            vec!["exe/cli"],
12347            vec!["exe/cli", "exe/serve"],
12348            vec!["exe/cli", "exe/cli"],
12349        ] {
12350            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12351            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12352            assert_eq!(
12353                c.exe(),
12354                expected.as_slice(),
12355                "Caixa::exe must return :exe verbatim (got {:?}, \
12356                 expected {expected:?})",
12357                c.exe(),
12358            );
12359            assert_eq!(
12360                c.exe(),
12361                c.exe.as_slice(),
12362                "Caixa::exe must byte-equal the raw \
12363                 `self.exe.as_slice()` field access across every value \
12364                 in the Vec<String> accept-set",
12365            );
12366        }
12367    }
12368
12369    #[test]
12370    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
12371        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12372        // empty-arm gate on the `:exe` slot must key off
12373        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
12374        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
12375        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
12376        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
12377        // (the canonical single-executable form every in-tree
12378        // `caixa_with_code_paths` positive control uses) must pass
12379        // validate. The pair jointly pins the accessor + validate-gate
12380        // composition: any future silent detour that had the accessor
12381        // return an empty slice on the `[""]` arm (a
12382        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
12383        // silently absorb the `CodePathEmpty` refusal at the accessor
12384        // boundary and the validate gate would accept a struct-literal
12385        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
12386        // catches that at caixa-core build time.
12387        //
12388        // Peer of the per-`Caixa`
12389        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12390        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
12391        // (b5d813f), and
12392        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12393        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12394        // composition axes — same "the validate / shape-gate predicate
12395        // must route through the substrate-primitive typed dispatch"
12396        // discipline extended onto the sibling outer top-level [`Caixa`]
12397        // `&[T]`-composition surface. Nominally the in-tree
12398        // `validate_code_paths` production body still keys off the
12399        // internal `[(":bibliotecas", &self.bibliotecas,
12400        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12401        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12402        // (the tuple's homogeneous slice-typed shape blocks a per-
12403        // element accessor swap in isolation — a future companion lift
12404        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
12405        // accessor axis closes that tuple onto the triple of typed
12406        // dispatches as a unit); the composition pin catches any future
12407        // accessor-side silent filter drop against that eventual tuple-
12408        // closure regardless of whether the `:exe` slot is threaded
12409        // through the accessor or the raw field access at the tuple's
12410        // construction site.
12411        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
12412        assert!(
12413            matches!(
12414                c.validate_code_paths(),
12415                Err(ManifestError::CodePathEmpty { slot: ":exe" })
12416            ),
12417            "validate_code_paths must reject exe == vec![\"\"] \
12418             with CodePathEmpty {{ slot: \":exe\" }} — the \
12419             accessor and the validate gate must route through the \
12420             same substrate-primitive typed dispatch on the \
12421             :exe per-entry empty arm",
12422        );
12423        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
12424        assert!(
12425            c.validate_code_paths().is_ok(),
12426            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
12427             (the canonical single-executable shape every in-tree \
12428             `caixa_with_code_paths` positive control uses)",
12429        );
12430    }
12431
12432    #[test]
12433    fn exe_projects_slice_by_borrow() {
12434        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
12435        // borrow — the returned slice borrows the underlying
12436        // `Vec<String>` storage of the `:exe` slot and the accessor
12437        // must not clone the backing `Vec` on every call. Peer of the
12438        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
12439        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
12440        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
12441        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
12442        // return axes — the accessor's returned slice must borrow from
12443        // `&self` (the returned reference's lifetime is tied to
12444        // `&self`), and calling the accessor twice on the same
12445        // [`Caixa`] must yield slices that are pointer-equal (the
12446        // underlying byte-buffer is the storage `Vec`'s allocation,
12447        // not a fresh copy) as well as value-equal (idempotent, no
12448        // side effects on `&self`).
12449        //
12450        // Pins against a future silent detour that returned an owned
12451        // `Vec<String>` (which would type-check but silently clone on
12452        // every call, breaking the zero-cost projection every peer
12453        // sibling slice accessor carries), a `&Vec<String>` return
12454        // (which would leak the backing `Vec`'s grow/push/reserve
12455        // surface no downstream consumer reaches for), or a one-arm-
12456        // only accessor that returned a saturating value on some
12457        // sentinel input (breaking the pass-through invariant the
12458        // sibling slice accessors carry).
12459        for exe in [
12460            vec![],
12461            vec!["exe/cli"],
12462            vec!["exe/cli", "exe/serve"],
12463            vec!["exe/cli", "exe/cli"],
12464        ] {
12465            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
12466            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
12467            let first = c.exe();
12468            let second = c.exe();
12469            assert_eq!(
12470                first, second,
12471                "Caixa::exe must be idempotent — two successive calls \
12472                 on the same &self must return the same &[String]",
12473            );
12474            assert_eq!(
12475                first.as_ptr(),
12476                second.as_ptr(),
12477                "Caixa::exe must borrow the underlying Vec<String> \
12478                 storage — two successive calls must return slices \
12479                 with the same backing pointer (a fresh Vec<String> \
12480                 clone would change the pointer on every call)",
12481            );
12482            assert_eq!(
12483                first,
12484                expected.as_slice(),
12485                "Caixa::exe must return :exe verbatim by borrow — \
12486                 got {first:?}, expected {expected:?}",
12487            );
12488        }
12489    }
12490
12491    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
12492
12493    #[test]
12494    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
12495        // The canonical per-`Caixa` `:servicos` universal-axis
12496        // ComputeUnit-CR-YAML-entry-path-list slice pin:
12497        // [`Caixa::servicos`] must return the `:servicos` typed
12498        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
12499        // the raw `self.servicos.as_slice()` access across every
12500        // representative value in the accept-set — `[]` (the "no
12501        // ComputeUnit-CR declared" arm every `:kind` other than
12502        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
12503        // `ServicoWithoutServicos` arm-gate fires exactly on this
12504        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
12505        // guard sentinel that pins the accessor doesn't perform a
12506        // silent `[""] → []` collapse on the empty-entry arm — validate
12507        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
12508        // but the accessor must ship the raw slot verbatim so a
12509        // validate-time gate regression surfaces at the layout /
12510        // per-Servico renderer boundary rather than being silently
12511        // absorbed into a component-drop),
12512        // `["servicos/demo.computeunit.yaml"]` (the canonical
12513        // singleton V0-shape every in-tree `caixa_with_code_paths`
12514        // positive control uses; the same shape
12515        // [`crate::require_single_servico`] admits),
12516        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
12517        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
12518        // singularity gate rejects through `ServicoCountMismatch
12519        // { count: 2 }` but the accessor must ship the raw slot
12520        // verbatim so struct-literal `Caixa { servicos: vec![...,
12521        // ...], .. }` fixtures continue to expose the count at the
12522        // accessor), and `["servicos/a.computeunit.yaml",
12523        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
12524        // sentinel — validate rejects through
12525        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
12526        // set-not-multiset gate, but the accessor must ship the raw
12527        // slot verbatim so struct-literal fixtures continue to expose
12528        // the duplicate at the accessor).
12529        //
12530        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
12531        // slice accessor pin on the substrate primitive — folds on the
12532        // "outer [`Caixa`] `&[T]` slice" projection pattern
12533        // `autores_returns_autores_slice_verbatim_across_permutations`
12534        // (b5d813f) opened,
12535        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12536        // (78c7d3c) folded on,
12537        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12538        // (8a36c23) closed the universal-axis text-tag family of, and
12539        // `exe_returns_exe_slice_verbatim_across_permutations`
12540        // (65d9527) opened the foreign-code-slot sub-family of. Closes
12541        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
12542        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
12543        // `:servicos`) now each carries a substrate-canonical slice
12544        // accessor. Pins against a future silent detour that returned
12545        // an owned `Vec<String>` (which would type-check but silently
12546        // clone on every accessor call, breaking the zero-cost
12547        // projection every peer sibling slice accessor carries), a
12548        // `[""] → []` collapse (which would silently absorb the
12549        // `CodePathEmpty` refusal case at the accessor boundary), an
12550        // `[a, a] → [a]` dedup collapse (which would silently absorb
12551        // the `CodePathDuplicate` refusal case at the accessor
12552        // boundary — the per-slot set-not-multiset gate is downstream
12553        // of the accessor and must not be silently promoted into it),
12554        // or a `[a, b] → [a]` singleton collapse (which would silently
12555        // absorb the V0 `ServicoCountMismatch` refusal case at the
12556        // accessor boundary — the V0 singularity gate is downstream of
12557        // the accessor and must not be silently promoted into it).
12558        for servicos in [
12559            vec![],
12560            vec![""],
12561            vec!["servicos/demo.computeunit.yaml"],
12562            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12563            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12564        ] {
12565            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12566            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12567            assert_eq!(
12568                c.servicos(),
12569                expected.as_slice(),
12570                "Caixa::servicos must return :servicos verbatim (got \
12571                 {:?}, expected {expected:?})",
12572                c.servicos(),
12573            );
12574            assert_eq!(
12575                c.servicos(),
12576                c.servicos.as_slice(),
12577                "Caixa::servicos must byte-equal the raw \
12578                 `self.servicos.as_slice()` field access across every \
12579                 value in the Vec<String> accept-set",
12580            );
12581        }
12582    }
12583
12584    #[test]
12585    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
12586        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
12587        // empty-arm gate on the `:servicos` slot must key off
12588        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
12589        // field-borrow walk. Structurally: a `Caixa { servicos:
12590        // vec!["".into()], .. }` must surface the `CodePathEmpty
12591        // { slot: ":servicos" }` refusal exactly, and a `Caixa
12592        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
12593        // .. }` (the canonical singleton V0-shape every in-tree
12594        // `caixa_with_code_paths` positive control uses) must pass
12595        // validate. The pair jointly pins the accessor + validate-gate
12596        // composition: any future silent detour that had the accessor
12597        // return an empty slice on the `[""]` arm (a `.iter().filter
12598        // (|s| !s.is_empty()).collect()` collapse) would silently
12599        // absorb the `CodePathEmpty` refusal at the accessor boundary
12600        // and the validate gate would accept a struct-literal
12601        // `Caixa { servicos: vec!["".into()], .. }` — the composition
12602        // pin catches that at caixa-core build time.
12603        //
12604        // Peer of the per-`Caixa`
12605        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12606        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12607        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
12608        // (b5d813f), and
12609        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12610        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
12611        // composition axes — same "the validate / shape-gate predicate
12612        // must route through the substrate-primitive typed dispatch"
12613        // discipline extended onto the sibling outer top-level
12614        // [`Caixa`] `&[T]`-composition surface, closing the trio of
12615        // code-surface accessor-composition pins on the same axis.
12616        // Nominally the in-tree `validate_code_paths` production body
12617        // still keys off the internal
12618        // `[(":bibliotecas", &self.bibliotecas,
12619        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
12620        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
12621        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
12622        // per-element accessor swap in isolation — a future companion
12623        // lift promotes the tuple's element type to `&[String]` and
12624        // threads the triple of typed dispatches through as a unit);
12625        // the composition pin catches any future accessor-side silent
12626        // filter drop against that eventual tuple-closure regardless
12627        // of whether the `:servicos` slot is threaded through the
12628        // accessor or the raw field access at the tuple's construction
12629        // site.
12630        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
12631        assert!(
12632            matches!(
12633                c.validate_code_paths(),
12634                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
12635            ),
12636            "validate_code_paths must reject servicos == vec![\"\"] \
12637             with CodePathEmpty {{ slot: \":servicos\" }} — the \
12638             accessor and the validate gate must route through the \
12639             same substrate-primitive typed dispatch on the \
12640             :servicos per-entry empty arm",
12641        );
12642        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
12643        assert!(
12644            c.validate_code_paths().is_ok(),
12645            "validate_code_paths must accept servicos == \
12646             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
12647             singleton V0-shape every in-tree `caixa_with_code_paths` \
12648             positive control uses)",
12649        );
12650    }
12651
12652    #[test]
12653    fn servicos_projects_slice_by_borrow() {
12654        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
12655        // borrow — the returned slice borrows the underlying
12656        // `Vec<String>` storage of the `:servicos` slot and the
12657        // accessor must not clone the backing `Vec` on every call.
12658        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
12659        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
12660        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
12661        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
12662        // the sibling outer top-level [`Caixa`] `&[String]`-return
12663        // axes — the accessor's returned slice must borrow from
12664        // `&self` (the returned reference's lifetime is tied to
12665        // `&self`), and calling the accessor twice on the same
12666        // [`Caixa`] must yield slices that are pointer-equal (the
12667        // underlying byte-buffer is the storage `Vec`'s allocation,
12668        // not a fresh copy) as well as value-equal (idempotent, no
12669        // side effects on `&self`).
12670        //
12671        // Pins against a future silent detour that returned an owned
12672        // `Vec<String>` (which would type-check but silently clone on
12673        // every call, breaking the zero-cost projection every peer
12674        // sibling slice accessor carries), a `&Vec<String>` return
12675        // (which would leak the backing `Vec`'s grow/push/reserve
12676        // surface no downstream consumer reaches for), or a one-arm-
12677        // only accessor that returned a saturating value on some
12678        // sentinel input (breaking the pass-through invariant the
12679        // sibling slice accessors carry).
12680        for servicos in [
12681            vec![],
12682            vec!["servicos/demo.computeunit.yaml"],
12683            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
12684            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
12685        ] {
12686            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
12687            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
12688            let first = c.servicos();
12689            let second = c.servicos();
12690            assert_eq!(
12691                first, second,
12692                "Caixa::servicos must be idempotent — two successive \
12693                 calls on the same &self must return the same &[String]",
12694            );
12695            assert_eq!(
12696                first.as_ptr(),
12697                second.as_ptr(),
12698                "Caixa::servicos must borrow the underlying \
12699                 Vec<String> storage — two successive calls must \
12700                 return slices with the same backing pointer (a fresh \
12701                 Vec<String> clone would change the pointer on every \
12702                 call)",
12703            );
12704            assert_eq!(
12705                first,
12706                expected.as_slice(),
12707                "Caixa::servicos must return :servicos verbatim by \
12708                 borrow — got {first:?}, expected {expected:?}",
12709            );
12710        }
12711    }
12712
12713    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
12714
12715    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
12716        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12717        c.deps = deps;
12718        c
12719    }
12720
12721    #[test]
12722    fn deps_returns_deps_slice_verbatim_across_permutations() {
12723        // The canonical per-`Caixa` `:deps` universal-axis runtime-
12724        // dependency-declaration-list slice pin: [`Caixa::deps`] must
12725        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
12726        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
12727        // access across every representative value in the accept-set —
12728        // `[]` (the "no runtime deps declared" arm every existing
12729        // fixture without a `:deps` line carries; the
12730        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
12731        // single-entry list (the shape most consumer caixas carry), a
12732        // canonical two-entry list (the multi-dep runtime closure), and
12733        // two past-the-guard sentinels — a `[""]`-`:nome` entry
12734        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12735        // `NomeInvalid` but the accessor must ship the raw slot
12736        // verbatim) and a `[a, a]` duplicate (validate rejects through
12737        // `DuplicateNome { list: ":deps" }` but the accessor must ship
12738        // the raw slot verbatim so struct-literal fixtures continue to
12739        // expose the duplicate at the accessor).
12740        //
12741        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
12742        // pin on the substrate primitive — opens the outer-`Caixa`
12743        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
12744        // future lift closes on. Peer of the closed outer-`Caixa`
12745        // foreign-code-slot `&[String]` sub-family
12746        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
12747        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
12748        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
12749        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
12750        // (`autores_returns_autores_slice_verbatim_across_permutations`
12751        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
12752        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
12753        // projection pattern onto a novel element-type axis (`Dep`
12754        // composite vs the prior sibling family's `String` scalar).
12755        // Pins against a future silent detour that returned an owned
12756        // `Vec<Dep>` (which would type-check but silently clone on every
12757        // accessor call, breaking the zero-cost projection every peer
12758        // sibling slice accessor carries), a `[""] → []` collapse (which
12759        // would silently absorb the `NomeEmpty` refusal case at the
12760        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12761        // would silently absorb the `DuplicateNome` refusal case at the
12762        // accessor boundary).
12763        for deps in [
12764            vec![],
12765            vec![Dep::simple("", "^0.1")],
12766            vec![Dep::simple("caixa-teia", "^0.1")],
12767            vec![
12768                Dep::simple("caixa-teia", "^0.1"),
12769                Dep::simple("caixa-core", "^0.1"),
12770            ],
12771            vec![
12772                Dep::simple("caixa-teia", "^0.1"),
12773                Dep::simple("caixa-teia", "^0.2"),
12774            ],
12775        ] {
12776            let c = caixa_with_deps(deps.clone());
12777            assert_eq!(
12778                c.deps(),
12779                deps.as_slice(),
12780                "Caixa::deps must return :deps verbatim (got {:?}, \
12781                 expected {deps:?})",
12782                c.deps(),
12783            );
12784            assert_eq!(
12785                c.deps(),
12786                c.deps.as_slice(),
12787                "Caixa::deps must element-equal the raw \
12788                 `self.deps.as_slice()` field access across every \
12789                 value in the Vec<Dep> accept-set",
12790            );
12791        }
12792    }
12793
12794    #[test]
12795    fn validate_deps_duplicate_arm_routes_through_accessor() {
12796        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
12797        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
12798        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
12799        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
12800        // "^0.2")], .. }` must surface the `DuplicateNome { list:
12801        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
12802        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
12803        // form) must pass validate. The pair jointly pins the accessor +
12804        // validate-gate composition: any future silent detour that had
12805        // the accessor return a dedupped slice on the `[a, a]` arm (a
12806        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12807        // would silently absorb the `DuplicateNome` refusal at the
12808        // accessor boundary and the validate gate would accept a
12809        // struct-literal `Caixa` carrying the drift — the composition
12810        // pin catches that at caixa-core build time.
12811        //
12812        // Peer of the per-`Caixa`
12813        // `validate_autores_empty_entry_arm_routes_through_accessor`
12814        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
12815        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
12816        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
12817        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
12818        // (611f78b) accessor-composition pins on the sibling `&[T]`-
12819        // composition axes — same "the validate gate must route through
12820        // the substrate-primitive typed dispatch" discipline extended
12821        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
12822        // composition surface, opening the outer-`Caixa` dependency-slot
12823        // arm of the composition-pin family.
12824        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
12825        let err = c.validate_deps().unwrap_err();
12826        assert!(
12827            matches!(
12828                err,
12829                DepError::DuplicateNome { ref nome, list } if nome == "d"
12830                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
12831            ),
12832            "validate_deps must reject deps == \
12833             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
12834             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
12835             accessor and the validate gate must route through the \
12836             same substrate-primitive typed dispatch on the :deps \
12837             within-list duplicate arm (got {err:?})",
12838        );
12839        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
12840        assert!(
12841            c.validate_deps().is_ok(),
12842            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
12843             (the canonical single-entry form)",
12844        );
12845    }
12846
12847    #[test]
12848    fn deps_projects_slice_by_borrow() {
12849        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
12850        // — the returned slice borrows the underlying `Vec<Dep>` storage
12851        // of the `:deps` slot and the accessor must not clone the
12852        // backing `Vec` on every call. Peer of the per-`Caixa`
12853        // `autores_projects_slice_by_borrow` (b5d813f),
12854        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
12855        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
12856        // `exe_projects_slice_by_borrow` (65d9527), and
12857        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
12858        // on the sibling outer top-level [`Caixa`] `&[String]`-return
12859        // axes — the accessor's returned slice must borrow from `&self`
12860        // (the returned reference's lifetime is tied to `&self`), and
12861        // calling the accessor twice on the same [`Caixa`] must yield
12862        // slices that are pointer-equal (the underlying byte-buffer is
12863        // the storage `Vec`'s allocation, not a fresh copy) as well as
12864        // value-equal (idempotent, no side effects on `&self`).
12865        //
12866        // Pins against a future silent detour that returned an owned
12867        // `Vec<Dep>` (which would type-check but silently clone on
12868        // every call), a `&Vec<Dep>` return (which would leak the
12869        // backing `Vec`'s grow/push/reserve surface no downstream
12870        // consumer reaches for), or a one-arm-only accessor that
12871        // returned a saturating value on some sentinel input.
12872        for deps in [
12873            vec![],
12874            vec![Dep::simple("caixa-teia", "^0.1")],
12875            vec![
12876                Dep::simple("caixa-teia", "^0.1"),
12877                Dep::simple("caixa-core", "^0.1"),
12878            ],
12879        ] {
12880            let c = caixa_with_deps(deps.clone());
12881            let first = c.deps();
12882            let second = c.deps();
12883            assert_eq!(
12884                first, second,
12885                "Caixa::deps must be idempotent — two successive calls \
12886                 on the same &self must return the same &[Dep]",
12887            );
12888            assert_eq!(
12889                first.as_ptr(),
12890                second.as_ptr(),
12891                "Caixa::deps must borrow the underlying Vec<Dep> \
12892                 storage — two successive calls must return slices \
12893                 with the same backing pointer (a fresh Vec<Dep> clone \
12894                 would change the pointer on every call)",
12895            );
12896            assert_eq!(
12897                first,
12898                deps.as_slice(),
12899                "Caixa::deps must return :deps verbatim by borrow — \
12900                 got {first:?}, expected {deps:?}",
12901            );
12902        }
12903    }
12904
12905    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
12906
12907    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
12908        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12909        c.deps_dev = deps_dev;
12910        c
12911    }
12912
12913    #[test]
12914    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
12915        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
12916        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
12917        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
12918        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
12919        // access across every representative value in the accept-set —
12920        // `[]` (the "no dev deps declared" arm every existing fixture
12921        // without a `:deps-dev` line carries; the [`Caixa::template`]
12922        // scaffold emits `:deps-dev ()`), a canonical single-entry list
12923        // (the shape most consumer caixas carry — a `tatara-check` dev
12924        // pin), a canonical two-entry list (the multi-dev-dep closure),
12925        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
12926        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
12927        // `NomeInvalid` but the accessor must ship the raw slot
12928        // verbatim) and a `[a, a]` duplicate (validate rejects through
12929        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
12930        // ship the raw slot verbatim so struct-literal fixtures continue
12931        // to expose the duplicate at the accessor).
12932        //
12933        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
12934        // pin on the substrate primitive — closes the outer-`Caixa`
12935        // dependency-slot `&[Dep]` sub-family the sibling
12936        // `deps_returns_deps_slice_verbatim_across_permutations`
12937        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
12938        // slice" projection pattern onto the sibling dev-dep axis —
12939        // pins against a future silent detour that returned an owned
12940        // `Vec<Dep>` (which would type-check but silently clone on every
12941        // accessor call, breaking the zero-cost projection every peer
12942        // sibling slice accessor carries), a `[""] → []` collapse (which
12943        // would silently absorb the `NomeEmpty` refusal case at the
12944        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
12945        // would silently absorb the `DuplicateNome` refusal case at the
12946        // accessor boundary).
12947        for deps_dev in [
12948            vec![],
12949            vec![Dep::simple("", "^0.1")],
12950            vec![Dep::simple("tatara-check", "^0.1")],
12951            vec![
12952                Dep::simple("tatara-check", "^0.1"),
12953                Dep::simple("caixa-lint", "^0.1"),
12954            ],
12955            vec![
12956                Dep::simple("tatara-check", "^0.1"),
12957                Dep::simple("tatara-check", "^0.2"),
12958            ],
12959        ] {
12960            let c = caixa_with_deps_dev(deps_dev.clone());
12961            assert_eq!(
12962                c.deps_dev(),
12963                deps_dev.as_slice(),
12964                "Caixa::deps_dev must return :deps-dev verbatim (got \
12965                 {:?}, expected {deps_dev:?})",
12966                c.deps_dev(),
12967            );
12968            assert_eq!(
12969                c.deps_dev(),
12970                c.deps_dev.as_slice(),
12971                "Caixa::deps_dev must element-equal the raw \
12972                 `self.deps_dev.as_slice()` field access across every \
12973                 value in the Vec<Dep> accept-set",
12974            );
12975        }
12976    }
12977
12978    #[test]
12979    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
12980        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
12981        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
12982        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
12983        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
12984        // Dep::simple("d", "^0.2")], .. }` must surface the
12985        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
12986        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
12987        // canonical single-entry form) must pass validate. The pair
12988        // jointly pins the accessor + validate-gate composition: any
12989        // future silent detour that had the accessor return a dedupped
12990        // slice on the `[a, a]` arm (a
12991        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
12992        // would silently absorb the `DuplicateNome` refusal at the
12993        // accessor boundary and the validate gate would accept a
12994        // struct-literal `Caixa` carrying the drift — the composition
12995        // pin catches that at caixa-core build time.
12996        //
12997        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
12998        // (ad34b4e) on the sibling `:deps` axis — same "the validate
12999        // gate must route through the substrate-primitive typed
13000        // dispatch" discipline folded onto the sibling `:deps-dev`
13001        // axis, closing the two-list dep-graph composition-pin family.
13002        // The `:deps-dev` diagnostic must carry the
13003        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
13004        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
13005        // offending list unambiguously.
13006        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
13007        let err = c.validate_deps().unwrap_err();
13008        assert!(
13009            matches!(
13010                err,
13011                DepError::DuplicateNome { ref nome, list } if nome == "d"
13012                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
13013            ),
13014            "validate_deps must reject deps_dev == \
13015             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
13016             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
13017             accessor and the validate gate must route through the \
13018             same substrate-primitive typed dispatch on the :deps-dev \
13019             within-list duplicate arm (got {err:?})",
13020        );
13021        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
13022        assert!(
13023            c.validate_deps().is_ok(),
13024            "validate_deps must accept deps_dev == \
13025             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
13026        );
13027    }
13028
13029    #[test]
13030    fn deps_dev_projects_slice_by_borrow() {
13031        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
13032        // borrow — the returned slice borrows the underlying `Vec<Dep>`
13033        // storage of the `:deps-dev` slot and the accessor must not
13034        // clone the backing `Vec` on every call. Peer of
13035        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
13036        // `:deps` axis, and of the per-`Caixa`
13037        // `autores_projects_slice_by_borrow` (b5d813f),
13038        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
13039        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
13040        // `exe_projects_slice_by_borrow` (65d9527), and
13041        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
13042        // on the sibling outer top-level [`Caixa`] `&[String]`-return
13043        // axes — the accessor's returned slice must borrow from `&self`
13044        // (the returned reference's lifetime is tied to `&self`), and
13045        // calling the accessor twice on the same [`Caixa`] must yield
13046        // slices that are pointer-equal (the underlying byte-buffer is
13047        // the storage `Vec`'s allocation, not a fresh copy) as well as
13048        // value-equal (idempotent, no side effects on `&self`).
13049        //
13050        // Pins against a future silent detour that returned an owned
13051        // `Vec<Dep>` (which would type-check but silently clone on
13052        // every call), a `&Vec<Dep>` return (which would leak the
13053        // backing `Vec`'s grow/push/reserve surface no downstream
13054        // consumer reaches for), or a one-arm-only accessor that
13055        // returned a saturating value on some sentinel input.
13056        for deps_dev in [
13057            vec![],
13058            vec![Dep::simple("tatara-check", "^0.1")],
13059            vec![
13060                Dep::simple("tatara-check", "^0.1"),
13061                Dep::simple("caixa-lint", "^0.1"),
13062            ],
13063        ] {
13064            let c = caixa_with_deps_dev(deps_dev.clone());
13065            let first = c.deps_dev();
13066            let second = c.deps_dev();
13067            assert_eq!(
13068                first, second,
13069                "Caixa::deps_dev must be idempotent — two successive \
13070                 calls on the same &self must return the same &[Dep]",
13071            );
13072            assert_eq!(
13073                first.as_ptr(),
13074                second.as_ptr(),
13075                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
13076                 storage — two successive calls must return slices \
13077                 with the same backing pointer (a fresh Vec<Dep> clone \
13078                 would change the pointer on every call)",
13079            );
13080            assert_eq!(
13081                first,
13082                deps_dev.as_slice(),
13083                "Caixa::deps_dev must return :deps-dev verbatim by \
13084                 borrow — got {first:?}, expected {deps_dev:?}",
13085            );
13086        }
13087    }
13088
13089    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
13090
13091    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
13092        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13093        c.limits = limits;
13094        c
13095    }
13096
13097    #[test]
13098    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
13099        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
13100        // composite optional-composite-reference-shape pin:
13101        // [`Caixa::limits`] must return the `:limits` typed
13102        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
13103        // reference over the same backing storage the raw
13104        // `self.limits.as_ref()` field access borrows from, byte-equal
13105        // across every representative fixture in the accept-set — the
13106        // author-omitted `None` shape (the "engine-default applies"
13107        // partition every downstream Servico M2 overlay emitter treats
13108        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
13109        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
13110        // per-axis cap is `None`, so the peer M2 overlay emitter's
13111        // `.is_empty()`-gated projection still emits nothing but the
13112        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
13113        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
13114        // fixture (only `:memory` set — the canonical shape most
13115        // memory-heavy Servicos carry), and a fully-populated composite
13116        // (every per-axis cap set — the canonical shape a
13117        // sandboxed-by-default Servico carries).
13118        //
13119        // Pins against a future silent detour that returned a fresh-
13120        // cloned [`LimitsSpec`] copy (which would type-check via the
13121        // `Clone` impl but silently break every downstream caller that
13122        // relied on the reference sharing the composite's backing
13123        // identity), a reference to an operator-resolved overlay (the
13124        // future per-cluster `:limits-overrides` slot — its resolution
13125        // must land at exactly this accessor body, not silently divert
13126        // the raw slot away from a second consumer), a
13127        // `None` → `Some(LimitsSpec::default)` cluster-default
13128        // projection (which would collapse the load-bearing
13129        // "author-omitted `:limits` ⇒ engine-default applies" partition
13130        // the peer [`crate::render::servico_m2_overlay`] emitter and
13131        // the peer [`Caixa::declared_servico_slots`] enumerator both
13132        // read), or an axis-shuffled projection (a future detour that
13133        // swapped `memory` and `fuel` through the accessor would
13134        // silently split the paired [`crate::StandardLayout::verify`]
13135        // per-`:limits` shape gate's traversal input from the peer
13136        // `servico_m2_overlay` emitter's projection input).
13137        //
13138        // First outer top-level [`Caixa`] `Option<&Composite>`-return
13139        // composite-reference accessor pin on the substrate primitive
13140        // — opens the outer-`Caixa` `Option<&Composite>` composite-
13141        // reference projection pattern the sibling `:behavior`
13142        // [`crate::BehaviorSpec`] / `:politicas`
13143        // [`crate::aplicacao::MeshPolicy`] / `:placement`
13144        // [`crate::aplicacao::Placement`] / `:entrada`
13145        // [`crate::aplicacao::Entrada`] future outer-composite lifts
13146        // fold on. Peer of the closed M3 outer-composite family the
13147        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
13148        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
13149        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
13150        // reference accessor pins already carry on the outer
13151        // [`crate::AplicacaoSpec`] altitude — extends the outer-
13152        // accessor byte-equal-projection discipline onto the outer
13153        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
13154        use crate::LimitsSpec;
13155        use std::time::Duration;
13156        let fixtures: Vec<Option<LimitsSpec>> = vec![
13157            None,
13158            Some(LimitsSpec::default()),
13159            Some(LimitsSpec {
13160                memory: Some(64 * 1024 * 1024),
13161                ..Default::default()
13162            }),
13163            Some(LimitsSpec {
13164                memory: Some(64 * 1024 * 1024),
13165                fuel: Some(1_000_000),
13166                wall_clock: Some(Duration::from_secs(30)),
13167                cpu: Some(500),
13168            }),
13169        ];
13170        for limits in fixtures {
13171            let c = caixa_with_limits(limits.clone());
13172            assert_eq!(
13173                c.limits(),
13174                limits.as_ref(),
13175                "Caixa::limits must return :limits verbatim (got {:?}, \
13176                 expected {:?})",
13177                c.limits(),
13178                limits.as_ref(),
13179            );
13180            match (c.limits(), c.limits.as_ref()) {
13181                (Some(a), Some(b)) => assert!(
13182                    std::ptr::eq(a, b),
13183                    "Caixa::limits accessor and self.limits.as_ref() \
13184                     field access must borrow the same backing storage \
13185                     — the accessor is the substrate-primitive typed \
13186                     dispatch every downstream Servico-M2-overlay \
13187                     composite consumer must route through, and a \
13188                     reference-identity split would silently break \
13189                     every consumer that relied on the borrow sharing \
13190                     the composite's storage",
13191                ),
13192                (None, None) => {}
13193                _ => panic!(
13194                    "Caixa::limits presence bit must byte-equal \
13195                     self.limits.is_some() — a presence-bit drift would \
13196                     silently split the paired StandardLayout::verify \
13197                     per-`:limits` shape gate's traversal head from \
13198                     the peer render::servico_m2_overlay M2 overlay \
13199                     emitter's traversal head from the peer \
13200                     Caixa::declared_servico_slots M2 declared-slot \
13201                     enumerator's presence probe",
13202                ),
13203            }
13204            assert_eq!(
13205                c.limits().is_some(),
13206                c.limits.is_some(),
13207                "Caixa::limits().is_some() must byte-equal \
13208                 self.limits.is_some() — a presence-bit drift would \
13209                 silently split every downstream Option<&LimitsSpec> \
13210                 consumer's partition on the engine-default arm",
13211            );
13212        }
13213    }
13214
13215    #[test]
13216    fn declared_servico_slots_limits_arm_routes_through_accessor() {
13217        // Composition pin: [`Caixa::declared_servico_slots`]'s
13218        // `:limits` presence-probe arm must key off [`Caixa::limits`],
13219        // not the raw `self.limits.is_some()` field-probe. Structurally:
13220        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
13221        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
13222        // (the presence bit is `Some`, so the M2 kind-coherence gate
13223        // must surface the slot as "declared" even when every per-axis
13224        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
13225        // push the label (the "author omitted the slot entirely"
13226        // partition). The pair jointly pins the accessor + declared-
13227        // slot enumerator composition: any future silent detour that
13228        // had the accessor collapse `Some(LimitsSpec::default())` to
13229        // `None` (a `.filter(|l| !l.is_empty())` projection) would
13230        // silently absorb the "declared but empty" arm at the
13231        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
13232        // kind-coherence gate would silently accept a
13233        // struct-literal `Caixa` carrying the drift.
13234        //
13235        // Peer of the sibling per-`Caixa`
13236        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
13237        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
13238        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
13239        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
13240        // enumerator gate must route through the substrate-primitive
13241        // typed dispatch" discipline extended onto the outer top-level
13242        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
13243        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
13244        // composition-pin family.
13245        use crate::LimitsSpec;
13246        let c = caixa_with_limits(Some(LimitsSpec::default()));
13247        let slots = c.declared_servico_slots();
13248        assert!(
13249            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13250            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
13251             when `:limits` is Some (even for LimitsSpec::default()) \
13252             — the accessor and the enumerator gate must route through \
13253             the same substrate-primitive typed dispatch on the outer \
13254             :limits presence bit (got slots={slots:?})",
13255        );
13256        let c = caixa_with_limits(None);
13257        let slots = c.declared_servico_slots();
13258        assert!(
13259            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
13260            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
13261             when `:limits` is None — the author-omitted arm must \
13262             route through the accessor's None-return unchanged (got \
13263             slots={slots:?})",
13264        );
13265    }
13266
13267    #[test]
13268    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
13269        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13270        // per-`:limits` M2 overlay emit arm must key off
13271        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
13272        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
13273        // Some(64 MiB), .. default }), .. }` must surface the
13274        // `M2_KEY_LIMITS` key with the per-axis
13275        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
13276        // limits: Some(LimitsSpec::default()), .. }` must omit the
13277        // key entirely (the `.is_empty()`-gated inner arm elides an
13278        // empty composite even when the outer presence bit is `Some`),
13279        // and a `Caixa { limits: None, .. }` must also omit the key
13280        // (the "author omitted the slot entirely" partition). The
13281        // three-fixture family jointly pins the accessor + M2 overlay
13282        // emitter composition: any future silent detour that had the
13283        // accessor return a fresh-cloned copy on the `Some` arm (a
13284        // `LimitsSpec::clone()` projection) would silently break the
13285        // reference-identity pin the peer per-axis
13286        // `serde_yaml::to_value(limits)` projection reads from.
13287        use crate::LimitsSpec;
13288        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
13289        let c = caixa_with_limits(Some(LimitsSpec {
13290            memory: Some(64 * 1024 * 1024),
13291            ..Default::default()
13292        }));
13293        let overlay = servico_m2_overlay(&c).unwrap();
13294        assert!(
13295            overlay.contains_key(M2_KEY_LIMITS),
13296            "servico_m2_overlay must surface M2_KEY_LIMITS when \
13297             `:limits` carries a non-empty composite — the accessor \
13298             and the M2 overlay emitter must route through the same \
13299             substrate-primitive typed dispatch on the outer :limits \
13300             composite (got overlay={overlay:?})",
13301        );
13302        let c = caixa_with_limits(Some(LimitsSpec::default()));
13303        let overlay = servico_m2_overlay(&c).unwrap();
13304        assert!(
13305            !overlay.contains_key(M2_KEY_LIMITS),
13306            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13307             `:limits` is Some(LimitsSpec::default()) — the empty \
13308             composite's `.is_empty()`-gated inner arm must elide \
13309             the key regardless of the outer presence bit (got \
13310             overlay={overlay:?})",
13311        );
13312        let c = caixa_with_limits(None);
13313        let overlay = servico_m2_overlay(&c).unwrap();
13314        assert!(
13315            !overlay.contains_key(M2_KEY_LIMITS),
13316            "servico_m2_overlay must omit M2_KEY_LIMITS when \
13317             `:limits` is None — the author-omitted arm must route \
13318             through the accessor's None-return unchanged (got \
13319             overlay={overlay:?})",
13320        );
13321    }
13322
13323    #[test]
13324    fn limits_projects_option_ref_by_borrow() {
13325        // The by-borrow pin: [`Caixa::limits`] returns
13326        // `Option<&LimitsSpec>` by borrow — the returned reference
13327        // borrows the underlying `Option<LimitsSpec>` storage of the
13328        // `:limits` slot and the accessor must not clone the backing
13329        // composite on every call. Peer of the sibling
13330        // `deps_projects_slice_by_borrow` (ad34b4e) /
13331        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
13332        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
13333        // extended here to the outer [`Caixa`] `Option<&Composite>`-
13334        // return axis: the accessor's returned reference must borrow
13335        // from `&self` (the returned reference's lifetime is tied to
13336        // `&self`), and calling the accessor twice on the same
13337        // [`Caixa`] must yield references that are pointer-equal (the
13338        // underlying byte-buffer is the storage `LimitsSpec`'s
13339        // allocation, not a fresh copy) as well as value-equal
13340        // (idempotent, no side effects on `&self`).
13341        //
13342        // Pins against a future silent detour that returned an owned
13343        // `LimitsSpec` (which would type-check via the `Clone` impl
13344        // but silently clone on every call), a `&LimitsSpec` panic-
13345        // return on the `None` arm (which would collapse the load-
13346        // bearing `Option` presence-bit into a runtime panic), or a
13347        // one-arm-only accessor that returned a saturating composite
13348        // on some sentinel input.
13349        use crate::LimitsSpec;
13350        use std::time::Duration;
13351        for limits in [
13352            Some(LimitsSpec::default()),
13353            Some(LimitsSpec {
13354                memory: Some(64 * 1024 * 1024),
13355                fuel: Some(1_000_000),
13356                wall_clock: Some(Duration::from_secs(30)),
13357                cpu: Some(500),
13358            }),
13359        ] {
13360            let c = caixa_with_limits(limits.clone());
13361            let first = c.limits().unwrap();
13362            let second = c.limits().unwrap();
13363            assert_eq!(
13364                first, second,
13365                "Caixa::limits must be idempotent — two successive \
13366                 calls on the same &self must return the same \
13367                 &LimitsSpec",
13368            );
13369            assert!(
13370                std::ptr::eq(first, second),
13371                "Caixa::limits must borrow the underlying \
13372                 Option<LimitsSpec> storage — two successive calls \
13373                 must return references with the same backing pointer \
13374                 (a fresh LimitsSpec clone would change the pointer \
13375                 on every call)",
13376            );
13377            assert_eq!(
13378                Some(first),
13379                limits.as_ref(),
13380                "Caixa::limits must return :limits verbatim by borrow \
13381                 — got {first:?}, expected {:?}",
13382                limits.as_ref(),
13383            );
13384        }
13385        let c = caixa_with_limits(None);
13386        assert!(
13387            c.limits().is_none(),
13388            "Caixa::limits must return None when :limits is absent — \
13389             the author-omitted arm must project through the \
13390             accessor's Option::None unchanged",
13391        );
13392    }
13393
13394    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
13395
13396    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
13397        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13398        c.behavior = behavior;
13399        c
13400    }
13401
13402    #[test]
13403    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
13404        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
13405        // composite optional-composite-reference-shape pin:
13406        // [`Caixa::behavior`] must return the `:behavior` typed
13407        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
13408        // reference over the same backing storage the raw
13409        // `self.behavior.as_ref()` field access borrows from, byte-equal
13410        // across every representative fixture in the accept-set — the
13411        // author-omitted `None` shape (the "runtime-default applies"
13412        // partition every downstream Servico M2 overlay emitter treats
13413        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
13414        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
13415        // every per-callback path is `None`, so the peer M2 overlay
13416        // emitter's `.is_empty()`-gated projection still emits nothing
13417        // but the outer presence-bit is `Some`, so
13418        // [`Caixa::declared_servico_slots`] still pushes the
13419        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
13420        // (only `:on-state-change` set — the canonical shape a caixa
13421        // that only wires the hot-upgrade migration path carries), and
13422        // a fully-populated composite (every per-callback path set —
13423        // the canonical shape a fully-instrumented gen_server-shaped
13424        // Servico carries).
13425        //
13426        // Peer of the sibling
13427        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13428        // (b2bd9d7) opening fixture-family + reference-identity +
13429        // presence-bit tetrad pin on the outer top-level [`Caixa`]
13430        // `Option<&Composite>`-return sub-family — extended here to the
13431        // second axis of that sub-family so both of the currently-lifted
13432        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
13433        // `:behavior`) carry the same "byte-equal, borrow-shared,
13434        // presence-bit-preserved" outer-accessor discipline.
13435        //
13436        // Pins against a future silent detour that returned a fresh-
13437        // cloned [`crate::BehaviorSpec`] copy (which would type-check
13438        // via the `Clone` impl but silently break every downstream
13439        // caller that relied on the reference sharing the composite's
13440        // backing identity), a reference to an operator-resolved
13441        // overlay (a future per-cluster `:behavior-overrides` slot —
13442        // its resolution must land at exactly this accessor body, not
13443        // silently divert the raw slot away from a second consumer), a
13444        // `None` → `Some(BehaviorSpec::default)` cluster-default
13445        // projection (which would collapse the load-bearing
13446        // "author-omitted `:behavior` ⇒ runtime-default applies"
13447        // partition the peer [`crate::render::servico_m2_overlay`]
13448        // emitter, the peer [`Caixa::declared_servico_slots`]
13449        // enumerator, and the cross-slot
13450        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
13451        // gate all read), or a callback-shuffled projection (a future
13452        // detour that swapped `on_init` and `on_terminate` through the
13453        // accessor would silently split the paired
13454        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
13455        // traversal input from the peer `servico_m2_overlay` emitter's
13456        // projection input from the cross-slot `:state-change`
13457        // composition gate's traversal input).
13458        use crate::BehaviorSpec;
13459        use std::path::PathBuf;
13460        let fixtures: Vec<Option<BehaviorSpec>> = vec![
13461            None,
13462            Some(BehaviorSpec::default()),
13463            Some(BehaviorSpec {
13464                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13465                ..Default::default()
13466            }),
13467            Some(BehaviorSpec {
13468                on_init: Some(PathBuf::from("lib/init.lisp")),
13469                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13470                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13471                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13472                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13473                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13474            }),
13475        ];
13476        for behavior in fixtures {
13477            let c = caixa_with_behavior(behavior.clone());
13478            assert_eq!(
13479                c.behavior(),
13480                behavior.as_ref(),
13481                "Caixa::behavior must return :behavior verbatim (got \
13482                 {:?}, expected {:?})",
13483                c.behavior(),
13484                behavior.as_ref(),
13485            );
13486            match (c.behavior(), c.behavior.as_ref()) {
13487                (Some(a), Some(b)) => assert!(
13488                    std::ptr::eq(a, b),
13489                    "Caixa::behavior accessor and self.behavior.as_ref() \
13490                     field access must borrow the same backing storage \
13491                     — the accessor is the substrate-primitive typed \
13492                     dispatch every downstream Servico-M2-overlay \
13493                     composite consumer must route through, and a \
13494                     reference-identity split would silently break \
13495                     every consumer that relied on the borrow sharing \
13496                     the composite's storage",
13497                ),
13498                (None, None) => {}
13499                _ => panic!(
13500                    "Caixa::behavior presence bit must byte-equal \
13501                     self.behavior.is_some() — a presence-bit drift \
13502                     would silently split the paired \
13503                     StandardLayout::verify per-`:behavior` shape \
13504                     gate's traversal head from the peer \
13505                     render::servico_m2_overlay M2 overlay emitter's \
13506                     traversal head from the cross-slot \
13507                     validate_upgrade_from_against_behavior \
13508                     composition gate's traversal head from the peer \
13509                     Caixa::declared_servico_slots M2 declared-slot \
13510                     enumerator's presence probe",
13511                ),
13512            }
13513            assert_eq!(
13514                c.behavior().is_some(),
13515                c.behavior.is_some(),
13516                "Caixa::behavior().is_some() must byte-equal \
13517                 self.behavior.is_some() — a presence-bit drift would \
13518                 silently split every downstream Option<&BehaviorSpec> \
13519                 consumer's partition on the runtime-default arm",
13520            );
13521        }
13522    }
13523
13524    #[test]
13525    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
13526        // Composition pin: [`Caixa::declared_servico_slots`]'s
13527        // `:behavior` presence-probe arm must key off
13528        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
13529        // field-probe. Structurally: a `Caixa { behavior:
13530        // Some(BehaviorSpec::default()), .. }` must still push
13531        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
13532        // presence bit is `Some`, so the M2 kind-coherence gate must
13533        // surface the slot as "declared" even when every per-callback
13534        // path is unset), and a `Caixa { behavior: None, .. }` must
13535        // NOT push the label (the "author omitted the slot entirely"
13536        // partition). The pair jointly pins the accessor + declared-
13537        // slot enumerator composition: any future silent detour that
13538        // had the accessor collapse `Some(BehaviorSpec::default())`
13539        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
13540        // silently absorb the "declared but empty" arm at the
13541        // accessor boundary and the
13542        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
13543        // kind-coherence gate would silently accept a struct-literal
13544        // `Caixa` carrying the drift.
13545        //
13546        // Peer of the sibling
13547        // `declared_servico_slots_limits_arm_routes_through_accessor`
13548        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13549        // `Option<&LimitsSpec>` arm of the same
13550        // [`Caixa::declared_servico_slots`] M2 declared-slot
13551        // enumerator's traversal — same "the enumerator gate must
13552        // route through the substrate-primitive typed dispatch"
13553        // discipline extended onto the outer top-level [`Caixa`]
13554        // `Option<&BehaviorSpec>`-composition surface.
13555        use crate::BehaviorSpec;
13556        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13557        let slots = c.declared_servico_slots();
13558        assert!(
13559            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13560            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
13561             when `:behavior` is Some (even for BehaviorSpec::default()) \
13562             — the accessor and the enumerator gate must route through \
13563             the same substrate-primitive typed dispatch on the outer \
13564             :behavior presence bit (got slots={slots:?})",
13565        );
13566        let c = caixa_with_behavior(None);
13567        let slots = c.declared_servico_slots();
13568        assert!(
13569            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
13570            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
13571             when `:behavior` is None — the author-omitted arm must \
13572             route through the accessor's None-return unchanged (got \
13573             slots={slots:?})",
13574        );
13575    }
13576
13577    #[test]
13578    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
13579        // Composition pin: [`crate::render::servico_m2_overlay`]'s
13580        // per-`:behavior` M2 overlay emit arm must key off
13581        // [`Caixa::behavior`], not the raw `&caixa.behavior`
13582        // field-borrow. Structurally: a `Caixa { behavior:
13583        // Some(BehaviorSpec { on_state_change: Some(...), .. default
13584        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
13585        // per-callback `onStateChange` sub-mapping in the overlay, a
13586        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
13587        // must omit the key entirely (the `.is_empty()`-gated inner
13588        // arm elides an empty composite even when the outer presence
13589        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
13590        // also omit the key (the "author omitted the slot entirely"
13591        // partition). The three-fixture family jointly pins the
13592        // accessor + M2 overlay emitter composition: any future
13593        // silent detour that had the accessor return a fresh-cloned
13594        // copy on the `Some` arm (a `BehaviorSpec::clone()`
13595        // projection) would silently break the reference-identity
13596        // pin the peer per-callback `serde_yaml::to_value(behavior)`
13597        // projection reads from.
13598        //
13599        // Peer of the sibling
13600        // `servico_m2_overlay_limits_arm_routes_through_accessor`
13601        // (b2bd9d7) composition pin on the sibling `:limits` outer-
13602        // `Option<&LimitsSpec>` arm of the same
13603        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
13604        // traversal — same "the emitter must route through the
13605        // substrate-primitive typed dispatch on the outer composite"
13606        // discipline extended onto the outer top-level [`Caixa`]
13607        // `Option<&BehaviorSpec>`-composition surface.
13608        use crate::BehaviorSpec;
13609        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
13610        use std::path::PathBuf;
13611        let c = caixa_with_behavior(Some(BehaviorSpec {
13612            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13613            ..Default::default()
13614        }));
13615        let overlay = servico_m2_overlay(&c).unwrap();
13616        assert!(
13617            overlay.contains_key(M2_KEY_BEHAVIOR),
13618            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
13619             `:behavior` carries a non-empty composite — the accessor \
13620             and the M2 overlay emitter must route through the same \
13621             substrate-primitive typed dispatch on the outer :behavior \
13622             composite (got overlay={overlay:?})",
13623        );
13624        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
13625        let overlay = servico_m2_overlay(&c).unwrap();
13626        assert!(
13627            !overlay.contains_key(M2_KEY_BEHAVIOR),
13628            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13629             `:behavior` is Some(BehaviorSpec::default()) — the empty \
13630             composite's `.is_empty()`-gated inner arm must elide the \
13631             key regardless of the outer presence bit (got \
13632             overlay={overlay:?})",
13633        );
13634        let c = caixa_with_behavior(None);
13635        let overlay = servico_m2_overlay(&c).unwrap();
13636        assert!(
13637            !overlay.contains_key(M2_KEY_BEHAVIOR),
13638            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
13639             `:behavior` is None — the author-omitted arm must route \
13640             through the accessor's None-return unchanged (got \
13641             overlay={overlay:?})",
13642        );
13643    }
13644
13645    #[test]
13646    fn behavior_projects_option_ref_by_borrow() {
13647        // The by-borrow pin: [`Caixa::behavior`] returns
13648        // `Option<&BehaviorSpec>` by borrow — the returned reference
13649        // borrows the underlying `Option<BehaviorSpec>` storage of the
13650        // `:behavior` slot and the accessor must not clone the backing
13651        // composite on every call. Peer of the sibling
13652        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
13653        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
13654        // return sub-family — extended here to the second axis of the
13655        // same sub-family: the accessor's returned reference must
13656        // borrow from `&self` (the returned reference's lifetime is
13657        // tied to `&self`), and calling the accessor twice on the same
13658        // [`Caixa`] must yield references that are pointer-equal (the
13659        // underlying byte-buffer is the storage `BehaviorSpec`'s
13660        // allocation, not a fresh copy) as well as value-equal
13661        // (idempotent, no side effects on `&self`).
13662        //
13663        // Pins against a future silent detour that returned an owned
13664        // `BehaviorSpec` (which would type-check via the `Clone` impl
13665        // but silently clone on every call), a `&BehaviorSpec` panic-
13666        // return on the `None` arm (which would collapse the load-
13667        // bearing `Option` presence-bit into a runtime panic), or a
13668        // one-arm-only accessor that returned a saturating composite
13669        // on some sentinel input.
13670        use crate::BehaviorSpec;
13671        use std::path::PathBuf;
13672        for behavior in [
13673            Some(BehaviorSpec::default()),
13674            Some(BehaviorSpec {
13675                on_init: Some(PathBuf::from("lib/init.lisp")),
13676                on_call: Some(PathBuf::from("lib/handlers.lisp")),
13677                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
13678                on_info: Some(PathBuf::from("lib/handlers.lisp")),
13679                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
13680                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
13681            }),
13682        ] {
13683            let c = caixa_with_behavior(behavior.clone());
13684            let first = c.behavior().unwrap();
13685            let second = c.behavior().unwrap();
13686            assert_eq!(
13687                first, second,
13688                "Caixa::behavior must be idempotent — two successive \
13689                 calls on the same &self must return the same \
13690                 &BehaviorSpec",
13691            );
13692            assert!(
13693                std::ptr::eq(first, second),
13694                "Caixa::behavior must borrow the underlying \
13695                 Option<BehaviorSpec> storage — two successive calls \
13696                 must return references with the same backing pointer \
13697                 (a fresh BehaviorSpec clone would change the pointer \
13698                 on every call)",
13699            );
13700            assert_eq!(
13701                Some(first),
13702                behavior.as_ref(),
13703                "Caixa::behavior must return :behavior verbatim by \
13704                 borrow — got {first:?}, expected {:?}",
13705                behavior.as_ref(),
13706            );
13707        }
13708        let c = caixa_with_behavior(None);
13709        assert!(
13710            c.behavior().is_none(),
13711            "Caixa::behavior must return None when :behavior is absent \
13712             — the author-omitted arm must project through the \
13713             accessor's Option::None unchanged",
13714        );
13715    }
13716
13717    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
13718
13719    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
13720        use crate::aplicacao::{Membro, WitContract};
13721        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13722        c.kind = CaixaKind::Aplicacao;
13723        c.membros = vec![Membro {
13724            caixa: "a".into(),
13725            versao: "^0.1".into(),
13726        }];
13727        c.contratos = vec![WitContract {
13728            de: "a".into(),
13729            para: "a".into(),
13730            wit: "wasi:http/proxy".into(),
13731            endpoint: Some("/x".into()),
13732            subject: None,
13733            slot: None,
13734        }];
13735        c.politicas = politicas;
13736        c
13737    }
13738
13739    #[test]
13740    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
13741        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
13742        // composite optional-composite-reference-shape pin:
13743        // [`Caixa::politicas`] must return the `:politicas` typed
13744        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
13745        // reference over the same backing storage the raw
13746        // `self.politicas.as_ref()` field access borrows from,
13747        // byte-equal across every representative fixture in the
13748        // accept-set — the author-omitted `None` shape (the "cluster-
13749        // default applies" partition every downstream mesh-artifact
13750        // emitter treats as "emit no `:politicas` overlay"), the
13751        // empty-composite `Some(MeshPolicy { .. default })` shape
13752        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
13753        // per-axis mesh-policy scalar is `None`, so the peer inner
13754        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
13755        // caixa-mesh overlay elides every per-axis emit but the outer
13756        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
13757        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
13758        // single-axis fixture (only `:timeout` set — the canonical
13759        // shape a latency-sensitive Aplicacao carries), and a
13760        // fully-populated composite (every per-axis mesh-policy
13761        // scalar set — the canonical shape a fully-governed
13762        // Aplicacao carries).
13763        //
13764        // Pins against a future silent detour that returned a fresh-
13765        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
13766        // type-check via the `Clone` impl but silently break every
13767        // downstream caller that relied on the reference sharing the
13768        // composite's backing identity), a reference to an operator-
13769        // resolved overlay (the future per-cluster
13770        // `:politicas-overrides` slot — its resolution must land at
13771        // exactly this accessor body, not silently divert the raw
13772        // slot away from the peer [`Caixa::declared_mesh_slots`]
13773        // enumerator's presence probe), a
13774        // `None` → `Some(MeshPolicy::default)` cluster-default
13775        // projection (which would collapse the load-bearing
13776        // "author-omitted `:politicas` ⇒ cluster-default applies"
13777        // partition the peer [`Caixa::declared_mesh_slots`]
13778        // enumerator and the peer [`Caixa::aplicacao_view`]
13779        // Aplicacao-composition seed both read), or an axis-shuffled
13780        // projection (a future detour that swapped `timeout` and
13781        // `retries` through the accessor would silently split the
13782        // paired [`Caixa::aplicacao_view`] seed's fold input from the
13783        // sibling M3 mesh-artifact emitter's projection input).
13784        //
13785        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
13786        // composite-reference accessor pin on the substrate primitive
13787        // — peer of the sibling
13788        // `limits_returns_limits_option_ref_verbatim_across_permutations`
13789        // (b2bd9d7) and
13790        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
13791        // (35d8b52) opening tetrad pins on the outer top-level
13792        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
13793        // here to the first of the three M3 mesh-slot axes so the
13794        // opening third of the outer `Option<&Composite>` sub-family
13795        // carries the same "byte-equal, borrow-shared, presence-bit-
13796        // preserved" outer-accessor discipline.
13797        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
13798        use std::time::Duration;
13799        let fixtures: Vec<Option<MeshPolicy>> = vec![
13800            None,
13801            Some(MeshPolicy::default()),
13802            Some(MeshPolicy {
13803                timeout: Some(Duration::from_secs(30)),
13804                ..Default::default()
13805            }),
13806            Some(MeshPolicy {
13807                timeout: Some(Duration::from_secs(30)),
13808                retries: Some(3),
13809                circuit_breaker: Some(CircuitBreaker {
13810                    max_failures: 5,
13811                    window: Duration::from_secs(60),
13812                }),
13813                mtls_required: Some(true),
13814                rate_limit: Some(RateLimit {
13815                    rate: 100,
13816                    window: Duration::from_secs(1),
13817                }),
13818            }),
13819        ];
13820        for politicas in fixtures {
13821            let c = caixa_aplicacao_with_politicas(politicas.clone());
13822            assert_eq!(
13823                c.politicas(),
13824                politicas.as_ref(),
13825                "Caixa::politicas must return :politicas verbatim (got \
13826                 {:?}, expected {:?})",
13827                c.politicas(),
13828                politicas.as_ref(),
13829            );
13830            match (c.politicas(), c.politicas.as_ref()) {
13831                (Some(a), Some(b)) => assert!(
13832                    std::ptr::eq(a, b),
13833                    "Caixa::politicas accessor and self.politicas.as_ref() \
13834                     field access must borrow the same backing storage \
13835                     — the accessor is the substrate-primitive typed \
13836                     dispatch every downstream Aplicacao-mesh-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::politicas presence bit must byte-equal \
13845                     self.politicas.is_some() — a presence-bit drift \
13846                     would silently split the paired \
13847                     Caixa::aplicacao_view Aplicacao-composition seed's \
13848                     traversal head from the peer \
13849                     Caixa::declared_mesh_slots M3 declared-slot \
13850                     enumerator's presence probe",
13851                ),
13852            }
13853            assert_eq!(
13854                c.politicas().is_some(),
13855                c.politicas.is_some(),
13856                "Caixa::politicas().is_some() must byte-equal \
13857                 self.politicas.is_some() — a presence-bit drift would \
13858                 silently split every downstream Option<&MeshPolicy> \
13859                 consumer's partition on the cluster-default arm",
13860            );
13861        }
13862    }
13863
13864    #[test]
13865    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
13866        // Composition pin: [`Caixa::declared_mesh_slots`]'s
13867        // `:politicas` presence-probe arm must key off
13868        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
13869        // field-probe. Structurally: a `Caixa { politicas:
13870        // Some(MeshPolicy::default()), .. }` must still push
13871        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
13872        // presence bit is `Some`, so the M3 kind-coherence gate must
13873        // surface the slot as "declared" even when every per-axis
13874        // scalar is unset), and a `Caixa { politicas: None, .. }` must
13875        // NOT 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(MeshPolicy::default())` to
13879        // `None` (a `.filter(|p| !p.is_empty())` projection) would
13880        // silently absorb the "declared but empty" arm at the
13881        // accessor boundary and the
13882        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
13883        // coherence gate would silently accept a struct-literal
13884        // `Caixa` carrying the drift.
13885        //
13886        // Peer of the sibling
13887        // `declared_servico_slots_limits_arm_routes_through_accessor`
13888        // (b2bd9d7) and
13889        // `declared_servico_slots_behavior_arm_routes_through_accessor`
13890        // (35d8b52) composition pins on the sibling `:limits` /
13891        // `:behavior` outer-`Option<&Composite>` arms of the peer
13892        // [`Caixa::declared_servico_slots`] M2 declared-slot
13893        // enumerator's traversal — same "the enumerator gate must
13894        // route through the substrate-primitive typed dispatch"
13895        // discipline extended onto the outer top-level [`Caixa`] M3
13896        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
13897        // enumerator carries the same routing invariant as its M2
13898        // sibling.
13899        use crate::aplicacao::MeshPolicy;
13900        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13901        let slots = c.declared_mesh_slots();
13902        assert!(
13903            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13904            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
13905             when `:politicas` is Some (even for MeshPolicy::default()) \
13906             — the accessor and the enumerator gate must route through \
13907             the same substrate-primitive typed dispatch on the outer \
13908             :politicas presence bit (got slots={slots:?})",
13909        );
13910        let c = caixa_aplicacao_with_politicas(None);
13911        let slots = c.declared_mesh_slots();
13912        assert!(
13913            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
13914            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
13915             when `:politicas` is None — the author-omitted arm must \
13916             route through the accessor's None-return unchanged (got \
13917             slots={slots:?})",
13918        );
13919    }
13920
13921    #[test]
13922    fn aplicacao_view_politicas_arm_folds_through_accessor() {
13923        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
13924        // Aplicacao-composition seed must fold through
13925        // [`Caixa::politicas`], not the raw
13926        // `self.politicas.clone().unwrap_or_default()` field-borrow.
13927        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
13928        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
13929        // must surface a projected [`crate::AplicacaoSpec`] whose
13930        // `politicas().timeout()` field byte-equals the outer
13931        // composite's `timeout` scalar (the fold must project the
13932        // authored composite verbatim), a `Caixa { politicas:
13933        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
13934        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
13935        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
13936        // fold's empty-composite arm collapses to the same default the
13937        // author-omitted arm does), and a `Caixa { politicas: None,
13938        // kind: Aplicacao, .. }` must surface an
13939        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
13940        // [`crate::aplicacao::MeshPolicy::default`] (the "author
13941        // omitted the slot entirely" arm folds through the
13942        // `unwrap_or_default` onto the cluster-default). The triad
13943        // jointly pins the accessor + Aplicacao-composition seed
13944        // composition: any future silent detour that had the accessor
13945        // divert the raw slot away from the seed's fold (an operator-
13946        // resolved overlay's default-fold arm silently differing from
13947        // the raw slot's default-fold arm) would silently split the
13948        // build-time mesh-artifact emission gate from the caixa-mesh
13949        // renderer's Aplicacao-view input at the composition boundary.
13950        use crate::aplicacao::MeshPolicy;
13951        use std::time::Duration;
13952        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
13953            timeout: Some(Duration::from_secs(30)),
13954            ..Default::default()
13955        }));
13956        let view = c.aplicacao_view().unwrap();
13957        assert_eq!(
13958            view.politicas().timeout(),
13959            Some(Duration::from_secs(30)),
13960            "Caixa::aplicacao_view must fold the authored :politicas \
13961             :timeout scalar through the accessor verbatim onto the \
13962             projected AplicacaoSpec — a future silent detour at the \
13963             seed's fold arm would surface here as a projected-scalar \
13964             drift (got {:?})",
13965            view.politicas().timeout(),
13966        );
13967        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
13968        let view = c.aplicacao_view().unwrap();
13969        assert_eq!(
13970            view.politicas(),
13971            &MeshPolicy::default(),
13972            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
13973             through the accessor onto MeshPolicy::default — the empty- \
13974             composite arm collapses to the same default the author- \
13975             omitted arm does (got {:?})",
13976            view.politicas(),
13977        );
13978        let c = caixa_aplicacao_with_politicas(None);
13979        let view = c.aplicacao_view().unwrap();
13980        assert_eq!(
13981            view.politicas(),
13982            &MeshPolicy::default(),
13983            "Caixa::aplicacao_view must fold None through the accessor's \
13984             unwrap_or_default onto MeshPolicy::default — the author- \
13985             omitted arm must route through the accessor's None-return \
13986             unchanged (got {:?})",
13987            view.politicas(),
13988        );
13989    }
13990
13991    #[test]
13992    fn politicas_projects_option_ref_by_borrow() {
13993        // The by-borrow pin: [`Caixa::politicas`] returns
13994        // `Option<&MeshPolicy>` by borrow — the returned reference
13995        // borrows the underlying `Option<MeshPolicy>` storage of the
13996        // `:politicas` slot and the accessor must not clone the
13997        // backing composite on every call. Peer of the sibling
13998        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
13999        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
14000        // pins on the outer top-level [`Caixa`]
14001        // `Option<&Composite>`-return sub-family — extended here to
14002        // the third axis of the same sub-family: the accessor's
14003        // returned reference must borrow from `&self` (the returned
14004        // reference's lifetime is tied to `&self`), and calling the
14005        // accessor twice on the same [`Caixa`] must yield references
14006        // that are pointer-equal (the underlying byte-buffer is the
14007        // storage `MeshPolicy`'s allocation, not a fresh copy) as
14008        // well as value-equal (idempotent, no side effects on
14009        // `&self`).
14010        //
14011        // Pins against a future silent detour that returned an owned
14012        // `MeshPolicy` (which would type-check via the `Clone` impl
14013        // but silently clone on every call), a `&MeshPolicy` panic-
14014        // return on the `None` arm (which would collapse the load-
14015        // bearing `Option` presence-bit into a runtime panic), or a
14016        // one-arm-only accessor that returned a saturating composite
14017        // on some sentinel input.
14018        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
14019        use std::time::Duration;
14020        for politicas in [
14021            Some(MeshPolicy::default()),
14022            Some(MeshPolicy {
14023                timeout: Some(Duration::from_secs(30)),
14024                retries: Some(3),
14025                circuit_breaker: Some(CircuitBreaker {
14026                    max_failures: 5,
14027                    window: Duration::from_secs(60),
14028                }),
14029                mtls_required: Some(true),
14030                rate_limit: Some(RateLimit {
14031                    rate: 100,
14032                    window: Duration::from_secs(1),
14033                }),
14034            }),
14035        ] {
14036            let c = caixa_aplicacao_with_politicas(politicas.clone());
14037            let first = c.politicas().unwrap();
14038            let second = c.politicas().unwrap();
14039            assert_eq!(
14040                first, second,
14041                "Caixa::politicas must be idempotent — two successive \
14042                 calls on the same &self must return the same \
14043                 &MeshPolicy",
14044            );
14045            assert!(
14046                std::ptr::eq(first, second),
14047                "Caixa::politicas must borrow the underlying \
14048                 Option<MeshPolicy> storage — two successive calls \
14049                 must return references with the same backing pointer \
14050                 (a fresh MeshPolicy clone would change the pointer on \
14051                 every call)",
14052            );
14053            assert_eq!(
14054                Some(first),
14055                politicas.as_ref(),
14056                "Caixa::politicas must return :politicas verbatim by \
14057                 borrow — got {first:?}, expected {:?}",
14058                politicas.as_ref(),
14059            );
14060        }
14061        let c = caixa_aplicacao_with_politicas(None);
14062        assert!(
14063            c.politicas().is_none(),
14064            "Caixa::politicas must return None when :politicas is \
14065             absent — the author-omitted arm must project through the \
14066             accessor's Option::None unchanged",
14067        );
14068    }
14069
14070    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
14071
14072    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
14073        use crate::aplicacao::{Membro, WitContract};
14074        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14075        c.kind = CaixaKind::Aplicacao;
14076        c.membros = vec![Membro {
14077            caixa: "a".into(),
14078            versao: "^0.1".into(),
14079        }];
14080        c.contratos = vec![WitContract {
14081            de: "a".into(),
14082            para: "a".into(),
14083            wit: "wasi:http/proxy".into(),
14084            endpoint: Some("/x".into()),
14085            subject: None,
14086            slot: None,
14087        }];
14088        c.placement = placement;
14089        c
14090    }
14091
14092    #[test]
14093    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
14094        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
14095        // composite optional-composite-reference-shape pin:
14096        // [`Caixa::placement`] must return the `:placement` typed
14097        // `Option<Placement>` verbatim as an `Option<&Placement>`
14098        // reference over the same backing storage the raw
14099        // `self.placement.as_ref()` field access borrows from,
14100        // byte-equal across every representative fixture in the
14101        // accept-set — the author-omitted `None` shape (the
14102        // "cluster-default applies" partition every downstream mesh-
14103        // artifact emitter treats as "emit no `:placement` overlay"),
14104        // the empty-composite `Some(Placement { .. default })` shape
14105        // (`estrategia: SingleNode`, empty clusters, no shard-key /
14106        // affinity — the outer presence-bit is `Some` so
14107        // [`Caixa::declared_mesh_slots`] still pushes the
14108        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
14109        // `Replicated`-on-two-clusters fixture (the canonical shape a
14110        // stateless HTTP Aplicacao carries), and a fully-populated
14111        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
14112        // shape a stateful Akka-style cluster-sharding Aplicacao
14113        // carries).
14114        //
14115        // Pins against a future silent detour that returned a fresh-
14116        // cloned [`crate::aplicacao::Placement`] copy (which would
14117        // type-check via the `Clone` impl but silently break every
14118        // downstream caller that relied on the reference sharing the
14119        // composite's backing identity), a reference to an operator-
14120        // resolved overlay (the future per-cluster
14121        // `:placement-overrides` slot — its resolution must land at
14122        // exactly this accessor body, not silently divert the raw
14123        // slot away from the peer [`Caixa::declared_mesh_slots`]
14124        // enumerator's presence probe), a `None` →
14125        // `Some(Placement::default)` cluster-default projection (which
14126        // would collapse the load-bearing "author-omitted `:placement`
14127        // ⇒ cluster-default applies" partition the peer
14128        // [`Caixa::declared_mesh_slots`] enumerator and the peer
14129        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
14130        // read), or an axis-shuffled projection (a future detour that
14131        // swapped `clusters` and `affinity` through the accessor would
14132        // silently split the paired [`Caixa::aplicacao_view`] seed's
14133        // fold input from the sibling M3 mesh-artifact emitter's
14134        // projection input).
14135        //
14136        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
14137        // composite-reference accessor pin on the substrate primitive
14138        // — peer of the sibling
14139        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14140        // (b2bd9d7),
14141        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14142        // (35d8b52), and
14143        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14144        // (5d23d29) opening triad pins on the outer top-level
14145        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14146        // here to the second of the three M3 mesh-slot axes so the
14147        // opening four-fifths of the outer `Option<&Composite>` sub-
14148        // family carries the same "byte-equal, borrow-shared,
14149        // presence-bit-preserved" outer-accessor discipline.
14150        use crate::aplicacao::{Placement, PlacementStrategy};
14151        let fixtures: Vec<Option<Placement>> = vec![
14152            None,
14153            Some(Placement::default()),
14154            Some(Placement {
14155                estrategia: PlacementStrategy::Replicated,
14156                clusters: vec!["rio".into(), "sao-paulo".into()],
14157                affinity: None,
14158                shard_key: None,
14159            }),
14160            Some(Placement {
14161                estrategia: PlacementStrategy::Sharded,
14162                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
14163                affinity: Some("data-locality".into()),
14164                shard_key: Some("$tenantId".into()),
14165            }),
14166        ];
14167        for placement in fixtures {
14168            let c = caixa_aplicacao_with_placement(placement.clone());
14169            assert_eq!(
14170                c.placement(),
14171                placement.as_ref(),
14172                "Caixa::placement must return :placement verbatim (got \
14173                 {:?}, expected {:?})",
14174                c.placement(),
14175                placement.as_ref(),
14176            );
14177            match (c.placement(), c.placement.as_ref()) {
14178                (Some(a), Some(b)) => assert!(
14179                    std::ptr::eq(a, b),
14180                    "Caixa::placement accessor and self.placement.as_ref() \
14181                     field access must borrow the same backing storage \
14182                     — the accessor is the substrate-primitive typed \
14183                     dispatch every downstream Aplicacao-distribution- \
14184                     overlay composite consumer must route through, and \
14185                     a reference-identity split would silently break \
14186                     every consumer that relied on the borrow sharing \
14187                     the composite's storage",
14188                ),
14189                (None, None) => {}
14190                _ => panic!(
14191                    "Caixa::placement presence bit must byte-equal \
14192                     self.placement.is_some() — a presence-bit drift \
14193                     would silently split the paired \
14194                     Caixa::aplicacao_view Aplicacao-composition seed's \
14195                     traversal head from the peer \
14196                     Caixa::declared_mesh_slots M3 declared-slot \
14197                     enumerator's presence probe",
14198                ),
14199            }
14200            assert_eq!(
14201                c.placement().is_some(),
14202                c.placement.is_some(),
14203                "Caixa::placement().is_some() must byte-equal \
14204                 self.placement.is_some() — a presence-bit drift would \
14205                 silently split every downstream Option<&Placement> \
14206                 consumer's partition on the cluster-default arm",
14207            );
14208        }
14209    }
14210
14211    #[test]
14212    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
14213        // Composition pin: [`Caixa::declared_mesh_slots`]'s
14214        // `:placement` presence-probe arm must key off
14215        // [`Caixa::placement`], not the raw `self.placement.is_some()`
14216        // field-probe. Structurally: a `Caixa { placement:
14217        // Some(Placement::default()), .. }` must still push
14218        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
14219        // presence bit is `Some`, so the M3 kind-coherence gate must
14220        // surface the slot as "declared" even when every per-axis
14221        // scalar defers to the cluster-default arm), and a `Caixa {
14222        // placement: None, .. }` must NOT push the label (the "author
14223        // omitted the slot entirely" partition). The pair jointly pins
14224        // the accessor + declared-slot enumerator composition: any
14225        // future silent detour that had the accessor collapse
14226        // `Some(Placement::default())` to `None` (a `.filter(|p|
14227        // p.clusters().is_empty().not())` projection) would silently
14228        // absorb the "declared but empty" arm at the accessor boundary
14229        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
14230        // kind-coherence gate would silently accept a struct-literal
14231        // `Caixa` carrying the drift.
14232        //
14233        // Peer of the sibling
14234        // `declared_servico_slots_limits_arm_routes_through_accessor`
14235        // (b2bd9d7),
14236        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14237        // (35d8b52), and
14238        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14239        // (5d23d29) composition pins on the sibling `:limits` /
14240        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
14241        // — same "the enumerator gate must route through the
14242        // substrate-primitive typed dispatch" discipline extended onto
14243        // the second of the three M3 mesh-slot axes so the
14244        // [`Caixa::declared_mesh_slots`] enumerator carries the same
14245        // routing invariant on the `:placement` arm as the peer
14246        // `:politicas` arm.
14247        use crate::aplicacao::Placement;
14248        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14249        let slots = c.declared_mesh_slots();
14250        assert!(
14251            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14252            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
14253             when `:placement` is Some (even for Placement::default()) \
14254             — the accessor and the enumerator gate must route through \
14255             the same substrate-primitive typed dispatch on the outer \
14256             :placement presence bit (got slots={slots:?})",
14257        );
14258        let c = caixa_aplicacao_with_placement(None);
14259        let slots = c.declared_mesh_slots();
14260        assert!(
14261            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
14262            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
14263             when `:placement` is None — the author-omitted arm must \
14264             route through the accessor's None-return unchanged (got \
14265             slots={slots:?})",
14266        );
14267    }
14268
14269    #[test]
14270    fn aplicacao_view_placement_arm_folds_through_accessor() {
14271        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
14272        // Aplicacao-composition seed must fold through
14273        // [`Caixa::placement`], not the raw
14274        // `self.placement.clone().unwrap_or_default()` field-borrow.
14275        // Structurally: a `Caixa { placement: Some(Placement {
14276        // estrategia: Replicated, clusters: ["rio"], .. default }),
14277        // kind: Aplicacao, .. }` must surface a projected
14278        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
14279        // `placement().clusters()` byte-equal the outer composite's
14280        // authored values (the fold must project the authored
14281        // composite verbatim), a `Caixa { placement:
14282        // Some(Placement::default()), kind: Aplicacao, .. }` must
14283        // surface an [`crate::AplicacaoSpec`] whose `placement()`
14284        // byte-equals [`crate::aplicacao::Placement::default`] (the
14285        // fold's empty-composite arm collapses to the same default
14286        // the author-omitted arm does), and a `Caixa { placement:
14287        // None, kind: Aplicacao, .. }` must surface an
14288        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
14289        // [`crate::aplicacao::Placement::default`] (the "author
14290        // omitted the slot entirely" arm folds through the
14291        // `unwrap_or_default` onto the cluster-default). The triad
14292        // jointly pins the accessor + Aplicacao-composition seed
14293        // composition: any future silent detour that had the accessor
14294        // divert the raw slot away from the seed's fold (an operator-
14295        // resolved overlay's default-fold arm silently differing from
14296        // the raw slot's default-fold arm) would silently split the
14297        // build-time distribution-artifact emission gate from the
14298        // caixa-mesh renderer's Aplicacao-view input at the
14299        // composition boundary.
14300        use crate::aplicacao::{Placement, PlacementStrategy};
14301        let c = caixa_aplicacao_with_placement(Some(Placement {
14302            estrategia: PlacementStrategy::Replicated,
14303            clusters: vec!["rio".into()],
14304            affinity: None,
14305            shard_key: None,
14306        }));
14307        let view = c.aplicacao_view().unwrap();
14308        assert_eq!(
14309            view.placement().estrategia(),
14310            PlacementStrategy::Replicated,
14311            "Caixa::aplicacao_view must fold the authored :placement \
14312             :estrategia scalar through the accessor verbatim onto the \
14313             projected AplicacaoSpec — a future silent detour at the \
14314             seed's fold arm would surface here as a projected-scalar \
14315             drift (got {:?})",
14316            view.placement().estrategia(),
14317        );
14318        assert_eq!(
14319            view.placement().clusters(),
14320            &["rio"],
14321            "Caixa::aplicacao_view must fold the authored :placement \
14322             :clusters list through the accessor verbatim onto the \
14323             projected AplicacaoSpec — a future silent detour at the \
14324             seed's fold arm would surface here as a projected-list \
14325             drift (got {:?})",
14326            view.placement().clusters(),
14327        );
14328        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
14329        let view = c.aplicacao_view().unwrap();
14330        assert_eq!(
14331            view.placement(),
14332            &Placement::default(),
14333            "Caixa::aplicacao_view must fold Some(Placement::default()) \
14334             through the accessor onto Placement::default — the empty- \
14335             composite arm collapses to the same default the author- \
14336             omitted arm does (got {:?})",
14337            view.placement(),
14338        );
14339        let c = caixa_aplicacao_with_placement(None);
14340        let view = c.aplicacao_view().unwrap();
14341        assert_eq!(
14342            view.placement(),
14343            &Placement::default(),
14344            "Caixa::aplicacao_view must fold None through the accessor's \
14345             unwrap_or_default onto Placement::default — the author- \
14346             omitted arm must route through the accessor's None-return \
14347             unchanged (got {:?})",
14348            view.placement(),
14349        );
14350    }
14351
14352    #[test]
14353    fn placement_projects_option_ref_by_borrow() {
14354        // The by-borrow pin: [`Caixa::placement`] returns
14355        // `Option<&Placement>` by borrow — the returned reference
14356        // borrows the underlying `Option<Placement>` storage of the
14357        // `:placement` slot and the accessor must not clone the
14358        // backing composite on every call. Peer of the sibling
14359        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14360        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
14361        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
14362        // pins on the outer top-level [`Caixa`]
14363        // `Option<&Composite>`-return sub-family — extended here to
14364        // the fourth axis of the same sub-family: the accessor's
14365        // returned reference must borrow from `&self` (the returned
14366        // reference's lifetime is tied to `&self`), and calling the
14367        // accessor twice on the same [`Caixa`] must yield references
14368        // that are pointer-equal (the underlying byte-buffer is the
14369        // storage `Placement`'s allocation, not a fresh copy) as well
14370        // as value-equal (idempotent, no side effects on `&self`).
14371        //
14372        // Pins against a future silent detour that returned an owned
14373        // `Placement` (which would type-check via the `Clone` impl
14374        // but silently clone on every call), a `&Placement` panic-
14375        // return on the `None` arm (which would collapse the load-
14376        // bearing `Option` presence-bit into a runtime panic), or a
14377        // one-arm-only accessor that returned a saturating composite
14378        // on some sentinel input.
14379        use crate::aplicacao::{Placement, PlacementStrategy};
14380        for placement in [
14381            Some(Placement::default()),
14382            Some(Placement {
14383                estrategia: PlacementStrategy::Sharded,
14384                clusters: vec!["rio".into(), "sao-paulo".into()],
14385                affinity: Some("data-locality".into()),
14386                shard_key: Some("$tenantId".into()),
14387            }),
14388        ] {
14389            let c = caixa_aplicacao_with_placement(placement.clone());
14390            let first = c.placement().unwrap();
14391            let second = c.placement().unwrap();
14392            assert_eq!(
14393                first, second,
14394                "Caixa::placement must be idempotent — two successive \
14395                 calls on the same &self must return the same \
14396                 &Placement",
14397            );
14398            assert!(
14399                std::ptr::eq(first, second),
14400                "Caixa::placement must borrow the underlying \
14401                 Option<Placement> storage — two successive calls \
14402                 must return references with the same backing pointer \
14403                 (a fresh Placement clone would change the pointer on \
14404                 every call)",
14405            );
14406            assert_eq!(
14407                Some(first),
14408                placement.as_ref(),
14409                "Caixa::placement must return :placement verbatim by \
14410                 borrow — got {first:?}, expected {:?}",
14411                placement.as_ref(),
14412            );
14413        }
14414        let c = caixa_aplicacao_with_placement(None);
14415        assert!(
14416            c.placement().is_none(),
14417            "Caixa::placement must return None when :placement is \
14418             absent — the author-omitted arm must project through the \
14419             accessor's Option::None unchanged",
14420        );
14421    }
14422
14423    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
14424
14425    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
14426        use crate::aplicacao::{Membro, WitContract};
14427        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14428        c.kind = CaixaKind::Aplicacao;
14429        c.membros = vec![Membro {
14430            caixa: "a".into(),
14431            versao: "^0.1".into(),
14432        }];
14433        c.contratos = vec![WitContract {
14434            de: "a".into(),
14435            para: "a".into(),
14436            wit: "wasi:http/proxy".into(),
14437            endpoint: Some("/x".into()),
14438            subject: None,
14439            slot: None,
14440        }];
14441        c.entrada = entrada;
14442        c
14443    }
14444
14445    #[test]
14446    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
14447        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
14448        // composite optional-composite-reference-shape pin:
14449        // [`Caixa::entrada`] must return the `:entrada` typed
14450        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
14451        // reference over the same backing storage the raw
14452        // `self.entrada.as_ref()` field access borrows from,
14453        // byte-equal across every representative fixture in the
14454        // accept-set — the author-omitted `None` shape (the
14455        // "cluster-internal Aplicacao" partition every downstream
14456        // Gateway-API emitter treats as "emit no listener + no
14457        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
14458        // (empty `paths` — the resolved-paths fallback the peer
14459        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
14460        // onto the substrate catch-all), and a fully-populated
14461        // multi-path-with-non-default-port fixture (the canonical
14462        // shape a public HTTP Aplicacao carries).
14463        //
14464        // Pins against a future silent detour that returned a fresh-
14465        // cloned [`crate::aplicacao::Entrada`] copy (which would
14466        // type-check via the `Clone` impl but silently break every
14467        // downstream caller that relied on the reference sharing the
14468        // composite's backing identity), a reference to an operator-
14469        // resolved overlay (the future per-cluster
14470        // `:entrada-overrides` slot — its resolution must land at
14471        // exactly this accessor body, not silently divert the raw
14472        // slot away from the peer [`Caixa::declared_mesh_slots`]
14473        // enumerator's presence probe), or an axis-shuffled projection
14474        // (a future detour that swapped `host` and `para` through the
14475        // accessor would silently split the paired
14476        // [`Caixa::aplicacao_view`] seed's forward input from the
14477        // sibling M3 gateway-artifact emitter's projection input).
14478        //
14479        // Fifth and final outer top-level [`Caixa`]
14480        // `Option<&Composite>`-return composite-reference accessor pin
14481        // on the substrate primitive — peer of the sibling
14482        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14483        // (b2bd9d7),
14484        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
14485        // (35d8b52),
14486        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
14487        // (5d23d29), and
14488        // `placement_returns_placement_option_ref_verbatim_across_permutations`
14489        // (4fb8074) opening tetrad pins on the outer top-level
14490        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
14491        // here to the third and final M3 mesh-slot axis so the closed
14492        // outer `Option<&Composite>` sub-family carries the same
14493        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
14494        // accessor discipline across all five arms.
14495        use crate::aplicacao::Entrada;
14496        let fixtures: Vec<Option<Entrada>> = vec![
14497            None,
14498            Some(Entrada {
14499                host: "checkout.quero.cloud".into(),
14500                para: "gateway".into(),
14501                paths: Vec::new(),
14502                port: crate::DEFAULT_SERVICO_PORT,
14503            }),
14504            Some(Entrada {
14505                host: "api.pleme.io".into(),
14506                para: "public-api".into(),
14507                paths: vec!["/v1".into(), "/v2".into()],
14508                port: 8080,
14509            }),
14510        ];
14511        for entrada in fixtures {
14512            let c = caixa_aplicacao_with_entrada(entrada.clone());
14513            assert_eq!(
14514                c.entrada(),
14515                entrada.as_ref(),
14516                "Caixa::entrada must return :entrada verbatim (got \
14517                 {:?}, expected {:?})",
14518                c.entrada(),
14519                entrada.as_ref(),
14520            );
14521            match (c.entrada(), c.entrada.as_ref()) {
14522                (Some(a), Some(b)) => assert!(
14523                    std::ptr::eq(a, b),
14524                    "Caixa::entrada accessor and self.entrada.as_ref() \
14525                     field access must borrow the same backing storage \
14526                     — the accessor is the substrate-primitive typed \
14527                     dispatch every downstream Aplicacao-external- \
14528                     gateway composite consumer must route through, and \
14529                     a reference-identity split would silently break \
14530                     every consumer that relied on the borrow sharing \
14531                     the composite's storage",
14532                ),
14533                (None, None) => {}
14534                _ => panic!(
14535                    "Caixa::entrada presence bit must byte-equal \
14536                     self.entrada.is_some() — a presence-bit drift \
14537                     would silently split the paired \
14538                     Caixa::aplicacao_view Aplicacao-composition seed's \
14539                     traversal head from the peer \
14540                     Caixa::declared_mesh_slots M3 declared-slot \
14541                     enumerator's presence probe",
14542                ),
14543            }
14544            assert_eq!(
14545                c.entrada().is_some(),
14546                c.entrada.is_some(),
14547                "Caixa::entrada().is_some() must byte-equal \
14548                 self.entrada.is_some() — a presence-bit drift would \
14549                 silently split every downstream Option<&Entrada> \
14550                 consumer's partition on the cluster-internal arm",
14551            );
14552        }
14553    }
14554
14555    #[test]
14556    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
14557        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
14558        // presence-probe arm must key off [`Caixa::entrada`], not the
14559        // raw `self.entrada.is_some()` field-probe. Structurally: a
14560        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
14561        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
14562        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
14563        // presence bit is `Some`, so the M3 kind-coherence gate must
14564        // surface the slot as "declared" even when every per-axis
14565        // scalar defers to the substrate catch-all / default port),
14566        // and a `Caixa { entrada: None, .. }` must NOT push the label
14567        // (the "author omitted the slot entirely" partition). The pair
14568        // jointly pins the accessor + declared-slot enumerator
14569        // composition: any future silent detour that had the accessor
14570        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
14571        // `.filter(|e| !e.paths.is_empty())` projection) would silently
14572        // absorb the "declared but empty-paths" arm at the accessor
14573        // boundary and the
14574        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
14575        // coherence gate would silently accept a struct-literal
14576        // `Caixa` carrying the drift.
14577        //
14578        // Peer of the sibling
14579        // `declared_servico_slots_limits_arm_routes_through_accessor`
14580        // (b2bd9d7),
14581        // `declared_servico_slots_behavior_arm_routes_through_accessor`
14582        // (35d8b52),
14583        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
14584        // (5d23d29), and
14585        // `declared_mesh_slots_placement_arm_routes_through_accessor`
14586        // (4fb8074) composition pins on the sibling `:limits` /
14587        // `:behavior` / `:politicas` / `:placement` outer-
14588        // `Option<&Composite>` arms — same "the enumerator gate must
14589        // route through the substrate-primitive typed dispatch"
14590        // discipline extended onto the third and final M3 mesh-slot
14591        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
14592        // carries the routing invariant on every M3 mesh-slot arm.
14593        use crate::aplicacao::Entrada;
14594        let c = caixa_aplicacao_with_entrada(Some(Entrada {
14595            host: "checkout.quero.cloud".into(),
14596            para: "gateway".into(),
14597            paths: Vec::new(),
14598            port: crate::DEFAULT_SERVICO_PORT,
14599        }));
14600        let slots = c.declared_mesh_slots();
14601        assert!(
14602            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14603            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
14604             `:entrada` is Some (even for empty-paths / default-port) \
14605             — the accessor and the enumerator gate must route through \
14606             the same substrate-primitive typed dispatch on the outer \
14607             :entrada presence bit (got slots={slots:?})",
14608        );
14609        let c = caixa_aplicacao_with_entrada(None);
14610        let slots = c.declared_mesh_slots();
14611        assert!(
14612            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
14613            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
14614             when `:entrada` is None — the author-omitted arm must \
14615             route through the accessor's None-return unchanged (got \
14616             slots={slots:?})",
14617        );
14618    }
14619
14620    #[test]
14621    fn aplicacao_view_entrada_arm_folds_through_accessor() {
14622        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
14623        // Aplicacao-composition seed must fold through
14624        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
14625        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
14626        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
14627        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
14628        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
14629        // equals the outer composite's authored value (the fold must
14630        // project the authored composite verbatim), and a `Caixa {
14631        // entrada: None, kind: Aplicacao, .. }` must surface an
14632        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
14633        // "author omitted the slot entirely" arm folds through the
14634        // accessor's `Option::cloned` onto the same `None` presence
14635        // bit — unlike the peer `:politicas` / `:placement` arms
14636        // `:entrada` has no cluster-default fold, the omitted arm
14637        // stays omitted). The pair jointly pins the accessor +
14638        // Aplicacao-composition seed composition: any future silent
14639        // detour that had the accessor divert the raw slot away from
14640        // the seed's fold (an operator-resolved overlay's forward arm
14641        // silently differing from the raw slot's forward arm) would
14642        // silently split the build-time gateway-artifact emission gate
14643        // from the caixa-mesh renderer's Aplicacao-view input at the
14644        // composition boundary.
14645        use crate::aplicacao::Entrada;
14646        let authored = Entrada {
14647            host: "api.pleme.io".into(),
14648            para: "public-api".into(),
14649            paths: vec!["/v1".into()],
14650            port: 8080,
14651        };
14652        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
14653        let view = c.aplicacao_view().unwrap();
14654        assert_eq!(
14655            view.entrada(),
14656            Some(&authored),
14657            "Caixa::aplicacao_view must fold the authored :entrada \
14658             composite through the accessor verbatim onto the \
14659             projected AplicacaoSpec — a future silent detour at the \
14660             seed's fold arm would surface here as a projected- \
14661             composite drift (got {:?})",
14662            view.entrada(),
14663        );
14664        let c = caixa_aplicacao_with_entrada(None);
14665        let view = c.aplicacao_view().unwrap();
14666        assert!(
14667            view.entrada().is_none(),
14668            "Caixa::aplicacao_view must fold None through the \
14669             accessor's Option::cloned onto None — the author- \
14670             omitted arm must route through the accessor's None-return \
14671             unchanged (got {:?})",
14672            view.entrada(),
14673        );
14674    }
14675
14676    #[test]
14677    fn entrada_projects_option_ref_by_borrow() {
14678        // The by-borrow pin: [`Caixa::entrada`] returns
14679        // `Option<&Entrada>` by borrow — the returned reference
14680        // borrows the underlying `Option<Entrada>` storage of the
14681        // `:entrada` slot and the accessor must not clone the backing
14682        // composite on every call. Peer of the sibling
14683        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
14684        // `behavior_projects_option_ref_by_borrow` (35d8b52),
14685        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
14686        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
14687        // borrow pins on the outer top-level [`Caixa`]
14688        // `Option<&Composite>`-return sub-family — extended here to
14689        // the fifth and final axis of the same sub-family, closing
14690        // the discipline: the accessor's returned reference must
14691        // borrow from `&self` (the returned reference's lifetime is
14692        // tied to `&self`), and calling the accessor twice on the
14693        // same [`Caixa`] must yield references that are pointer-equal
14694        // (the underlying byte-buffer is the storage `Entrada`'s
14695        // allocation, not a fresh copy) as well as value-equal
14696        // (idempotent, no side effects on `&self`).
14697        //
14698        // Pins against a future silent detour that returned an owned
14699        // `Entrada` (which would type-check via the `Clone` impl but
14700        // silently clone on every call), a `&Entrada` panic-return on
14701        // the `None` arm (which would collapse the load-bearing
14702        // `Option` presence-bit into a runtime panic), or a one-arm-
14703        // only accessor that returned a saturating composite on some
14704        // sentinel input.
14705        use crate::aplicacao::Entrada;
14706        for entrada in [
14707            Some(Entrada {
14708                host: "checkout.quero.cloud".into(),
14709                para: "gateway".into(),
14710                paths: Vec::new(),
14711                port: crate::DEFAULT_SERVICO_PORT,
14712            }),
14713            Some(Entrada {
14714                host: "api.pleme.io".into(),
14715                para: "public-api".into(),
14716                paths: vec!["/v1".into(), "/v2".into()],
14717                port: 8080,
14718            }),
14719        ] {
14720            let c = caixa_aplicacao_with_entrada(entrada.clone());
14721            let first = c.entrada().unwrap();
14722            let second = c.entrada().unwrap();
14723            assert_eq!(
14724                first, second,
14725                "Caixa::entrada must be idempotent — two successive \
14726                 calls on the same &self must return the same &Entrada",
14727            );
14728            assert!(
14729                std::ptr::eq(first, second),
14730                "Caixa::entrada must borrow the underlying \
14731                 Option<Entrada> storage — two successive calls must \
14732                 return references with the same backing pointer (a \
14733                 fresh Entrada clone would change the pointer on every \
14734                 call)",
14735            );
14736            assert_eq!(
14737                Some(first),
14738                entrada.as_ref(),
14739                "Caixa::entrada must return :entrada verbatim by \
14740                 borrow — got {first:?}, expected {:?}",
14741                entrada.as_ref(),
14742            );
14743        }
14744        let c = caixa_aplicacao_with_entrada(None);
14745        assert!(
14746            c.entrada().is_none(),
14747            "Caixa::entrada must return None when :entrada is absent \
14748             — the author-omitted arm must project through the \
14749             accessor's Option::None unchanged",
14750        );
14751    }
14752
14753    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
14754
14755    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
14756        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14757        c.estrategia = estrategia;
14758        c
14759    }
14760
14761    #[test]
14762    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
14763        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
14764        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
14765        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
14766        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
14767        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
14768        // over the same discriminant the raw `self.estrategia` field
14769        // access carries, byte-equal across every representative fixture
14770        // in the accept-set — the author-omitted `None` shape (the
14771        // "defer to [`RestartStrategy::default`] through the
14772        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
14773        // every non-`Supervisor`-kind `defcaixa` carries by
14774        // `#[serde(default)]`), and each of the four closed-set variants
14775        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
14776        // / [`RestartStrategy::RestForOne`] /
14777        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
14778        // partitions on.
14779        //
14780        // Pins against a future silent detour that re-derived the
14781        // strategy from a peer axis (an accidental fallback to
14782        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
14783        // collapse that read the outer `:children` list-length axis into
14784        // the strategy discriminator at the accessor boundary), a
14785        // stale-derive detour that substituted [`RestartStrategy::default`]
14786        // when the outer `Option` held `None` (which would silently
14787        // collapse the load-bearing "author explicitly declared
14788        // `:estrategia OneForOne`" vs "author omitted the slot and
14789        // inherited the default" partition the [`Self::declared_supervisor_slots`]
14790        // presence-probe reads — the enumerator gate would still push
14791        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
14792        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
14793        // kind-coherence gate's traversal head from the
14794        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
14795        // composition head), a reference to an operator-resolved overlay
14796        // (the future per-cluster `:estrategia-overrides` slot — its
14797        // resolution must land at exactly this accessor body, not
14798        // silently divert the raw slot away from a second consumer), or
14799        // an axis-remap projection (a future detour that mapped
14800        // `OneForAll` through the accessor onto `OneForOne` would
14801        // silently split every downstream sibling-restart-strategy
14802        // consumer's per-arm fan-out).
14803        //
14804        // First outer top-level [`Caixa`] `Option<Copy>`-return
14805        // supervisor-tree-slot flat-spread accessor pin on the substrate
14806        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
14807        // projection pattern the sibling per-`Caixa` `:max-restarts` /
14808        // `:restart-window` future outer-scalar pins fold on. Peer of
14809        // the inner-altitude
14810        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14811        // (eafb619) pin on the post-composition [`SupervisorSpec`]
14812        // altitude — same "the substrate-primitive accessor must byte-
14813        // equal the raw field access verbatim across every author-
14814        // declared value" discipline extended onto the pre-composition
14815        // outer author-surface [`Caixa`] altitude. Peer of the closed
14816        // outer-`Caixa` `Option<&Composite>` composite-reference family
14817        // the sibling `limits` / `behavior` / `politicas` / `placement` /
14818        // `entrada`
14819        // `..._returns_..._option_ref_verbatim_across_permutations` pins
14820        // already carry on the outer `Option<&Composite>` altitude.
14821        use crate::supervisor::RestartStrategy;
14822        let fixtures: Vec<Option<RestartStrategy>> = vec![
14823            None,
14824            Some(RestartStrategy::OneForOne),
14825            Some(RestartStrategy::OneForAll),
14826            Some(RestartStrategy::RestForOne),
14827            Some(RestartStrategy::SimpleOneForOne),
14828        ];
14829        for estrategia in fixtures {
14830            let c = caixa_with_estrategia(estrategia);
14831            assert_eq!(
14832                c.estrategia(),
14833                estrategia,
14834                "Caixa::estrategia must return :estrategia verbatim (got \
14835                 {:?}, expected {:?})",
14836                c.estrategia(),
14837                estrategia,
14838            );
14839            assert_eq!(
14840                c.estrategia(),
14841                c.estrategia,
14842                "Caixa::estrategia accessor and self.estrategia field \
14843                 access must byte-equal — the accessor is the substrate-\
14844                 primitive typed dispatch every downstream supervisor-\
14845                 tree flat-spread consumer must route through, and a \
14846                 discriminant split would silently break every consumer \
14847                 that relied on the accessor sharing the field's own \
14848                 Option<Copy> shape",
14849            );
14850            assert_eq!(
14851                c.estrategia().is_some(),
14852                c.estrategia.is_some(),
14853                "Caixa::estrategia().is_some() must byte-equal \
14854                 self.estrategia.is_some() — a presence-bit drift would \
14855                 silently split the paired Caixa::declared_supervisor_slots \
14856                 presence-probe arm from the Caixa::supervisor_view \
14857                 unwrap_or_default() fold's composition input",
14858            );
14859        }
14860    }
14861
14862    #[test]
14863    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
14864        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
14865        // `:estrategia` presence-probe arm must key off
14866        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
14867        // field-probe. Structurally: every `Caixa { estrategia:
14868        // Some(RestartStrategy::_), .. }` variant must push
14869        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
14870        // (the presence bit is `Some` for every closed-set variant, so
14871        // the M2 supervisor-tree kind-coherence gate must surface the
14872        // slot as "declared" regardless of which variant the author
14873        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
14874        // the label (the "author omitted the slot entirely, deferring
14875        // to [`RestartStrategy::default`] through the supervisor_view
14876        // fold" partition). The pair jointly pins the accessor +
14877        // declared-slot enumerator composition: any future silent detour
14878        // that had the accessor collapse `Some(RestartStrategy::default())`
14879        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
14880        // projection) would silently absorb the "declared but default-
14881        // valued" arm at the accessor boundary and the
14882        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
14883        // coherence gate would silently accept a struct-literal `Caixa`
14884        // carrying the drift.
14885        //
14886        // Peer of the sibling per-`Caixa`
14887        // `declared_servico_slots_limits_arm_routes_through_accessor`
14888        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
14889        // `Option<&LimitsSpec>` composition axis — same "the enumerator
14890        // gate must route through the substrate-primitive typed
14891        // dispatch" discipline extended onto the flat-spread M2
14892        // supervisor-tree `Option<RestartStrategy>`-composition surface,
14893        // opening the outer-`Caixa` supervisor-tree-slot arm of the
14894        // composition-pin family.
14895        use crate::supervisor::RestartStrategy;
14896        for estrategia in [
14897            RestartStrategy::OneForOne,
14898            RestartStrategy::OneForAll,
14899            RestartStrategy::RestForOne,
14900            RestartStrategy::SimpleOneForOne,
14901        ] {
14902            let c = caixa_with_estrategia(Some(estrategia));
14903            let slots = c.declared_supervisor_slots();
14904            assert!(
14905                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14906                "declared_supervisor_slots must push \
14907                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
14908                 Some({estrategia:?}) — the accessor and the enumerator \
14909                 gate must route through the same substrate-primitive \
14910                 typed dispatch on the outer :estrategia presence bit \
14911                 (got slots={slots:?})",
14912            );
14913        }
14914        let c = caixa_with_estrategia(None);
14915        let slots = c.declared_supervisor_slots();
14916        assert!(
14917            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
14918            "declared_supervisor_slots must NOT push \
14919             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
14920             — the author-omitted arm must route through the accessor's \
14921             None-return unchanged (got slots={slots:?})",
14922        );
14923    }
14924
14925    #[test]
14926    fn supervisor_view_estrategia_arm_routes_through_accessor() {
14927        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
14928        // [`SupervisorSpec`] construction arm must key off
14929        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
14930        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
14931        // for every `:kind Supervisor` `Caixa` carrying an author-
14932        // declared `Some(RestartStrategy::_)` variant, the composed
14933        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
14934        // outer accessor's declared variant unchanged; and for a
14935        // `:kind Supervisor` `Caixa` carrying `None`, the composed
14936        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
14937        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
14938        // arm the flat-spread `unwrap_or_default()` fold projects to on
14939        // the author-omitted arm — this is the *composition* between the
14940        // outer `Option<RestartStrategy>` accessor's presence-bit
14941        // surface and the inner post-composition non-`Option`
14942        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
14943        // pins the accessor + supervisor_view composition: any future
14944        // silent detour that had the accessor promote `None` to
14945        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
14946        // projection) would silently collapse the two arms into one at
14947        // the accessor boundary and the [`Self::declared_supervisor_slots`]
14948        // presence probe would silently drift from the composition site.
14949        //
14950        // Peer of the sibling M2 supervisor-slot post-composition
14951        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
14952        // pin on the [`SupervisorSpec::validate`] altitude — this pin
14953        // extends that inner-altitude accessor-routing discipline onto
14954        // the pre-composition outer author-surface [`Caixa`] altitude,
14955        // pinning the composition edge between the flat-spread outer
14956        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
14957        // `RestartStrategy` axes.
14958        use crate::CaixaKind;
14959        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
14960        for estrategia in [
14961            RestartStrategy::OneForOne,
14962            RestartStrategy::OneForAll,
14963            RestartStrategy::RestForOne,
14964            RestartStrategy::SimpleOneForOne,
14965        ] {
14966            let mut c = caixa_with_estrategia(Some(estrategia));
14967            c.kind = CaixaKind::Supervisor;
14968            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
14969            // shape partition through the [`gen_platform::IsVariant`]
14970            // derive-generated
14971            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
14972            // than the raw `matches!(estrategia, RestartStrategy::
14973            // SimpleOneForOne)` open-coded pattern-match — same closed-
14974            // set-typed-enum arm-discriminator dispatch discipline the
14975            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
14976            // convergence (915a934) extended onto its two paired positive
14977            // / negated `matches!` sites and the peer
14978            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
14979            // predicate convergence (766ec63) extended onto the M3 mesh-
14980            // slot per-`:placement` distribution-strategy discriminator
14981            // axis. See the sibling `supervisor::tests::
14982            // round_trip_all_strategies` and
14983            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
14984            // fixtures — the three sites (all test-only,
14985            // acknowledged in 915a934's Prior-commits footnote as the
14986            // outstanding follow-up) now consult one typed dispatch on
14987            // the substrate primitive.
14988            c.children = if estrategia.is_simple_one_for_one() {
14989                Vec::new()
14990            } else {
14991                vec![ChildSpec {
14992                    caixa: "worker".into(),
14993                    versao: "^0.1".into(),
14994                    restart: RestartPolicy::Permanent,
14995                }]
14996            };
14997            let view = c.supervisor_view().expect(
14998                "supervisor_view must materialize a SupervisorSpec for a \
14999                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
15000            );
15001            assert_eq!(
15002                view.estrategia(),
15003                c.estrategia().unwrap(),
15004                "supervisor_view must carry the outer Caixa::estrategia() \
15005                 declared variant onto the composed SupervisorSpec.estrategia \
15006                 field verbatim on the Some arm (got {:?}, expected {:?})",
15007                view.estrategia(),
15008                c.estrategia().unwrap(),
15009            );
15010        }
15011        // The author-omitted arm: outer `None` → composed
15012        // `RestartStrategy::default()` through the flat-spread
15013        // `unwrap_or_default()` fold.
15014        let mut c = caixa_with_estrategia(None);
15015        c.kind = CaixaKind::Supervisor;
15016        // Populate children so the sibling supervisor slots are coherent
15017        // for the [`Self::supervisor_view`] projection; the `:estrategia`
15018        // arm still defers to [`RestartStrategy::default`] on the
15019        // author-omitted arm even when the sibling slots carry values.
15020        c.children = vec![ChildSpec {
15021            caixa: "worker".into(),
15022            versao: "^0.1".into(),
15023            restart: RestartPolicy::Permanent,
15024        }];
15025        let view = c.supervisor_view().expect(
15026            "supervisor_view must materialize a SupervisorSpec for a \
15027             :kind Supervisor Caixa carrying a None `:estrategia` slot",
15028        );
15029        assert_eq!(
15030            view.estrategia(),
15031            RestartStrategy::default(),
15032            "supervisor_view must project the outer Caixa::estrategia() \
15033             None arm onto RestartStrategy::default() through the flat-\
15034             spread unwrap_or_default() fold (got {:?}, expected {:?})",
15035            view.estrategia(),
15036            RestartStrategy::default(),
15037        );
15038        assert!(
15039            c.estrategia().is_none(),
15040            "Caixa::estrategia() must remain None on the author-omitted \
15041             arm — the supervisor_view fold must not mutate the outer \
15042             flat-spread presence bit",
15043        );
15044    }
15045
15046    #[test]
15047    fn estrategia_projects_option_by_copy() {
15048        // The by-`Copy` pin: [`Caixa::estrategia`] returns
15049        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
15050        // the accessor does not borrow `&self` past the call (no
15051        // lifetime on the return type), and calling the accessor twice
15052        // on the same [`Caixa`] must yield discriminant-equal values
15053        // (idempotent, no side effects on `&self`). Peer of the sibling
15054        // outer-`Caixa` `Option<&Composite>` by-borrow
15055        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
15056        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
15057        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
15058        // `placement_projects_option_ref_by_borrow` (4fb8074) /
15059        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
15060        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
15061        // extended here to the outer-`Caixa` `Option<Copy>`-return
15062        // flat-spread axis. The `Copy` discipline replaces the pointer-
15063        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
15064        // `Copy` discriminant is definitionally the same discriminant, so
15065        // the axis reduces to discriminant equality).
15066        //
15067        // Pins against a future silent detour that returned a fresh
15068        // `Option<&RestartStrategy>` (which would type-check but silently
15069        // introduce a borrow of `&self` past the call, collapsing the
15070        // load-bearing "no lifetime on the return type" `Copy` projection
15071        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
15072        // read side effect that flipped the outer discriminant on
15073        // successive calls, or an axis-remap projection that returned a
15074        // different variant than the field storage.
15075        use crate::supervisor::RestartStrategy;
15076        for estrategia in [
15077            Some(RestartStrategy::OneForOne),
15078            Some(RestartStrategy::OneForAll),
15079            Some(RestartStrategy::RestForOne),
15080            Some(RestartStrategy::SimpleOneForOne),
15081        ] {
15082            let c = caixa_with_estrategia(estrategia);
15083            let first = c.estrategia();
15084            let second = c.estrategia();
15085            assert_eq!(
15086                first, second,
15087                "Caixa::estrategia must be idempotent — two successive \
15088                 calls on the same &self must return the same \
15089                 Option<RestartStrategy>",
15090            );
15091            assert_eq!(
15092                first, estrategia,
15093                "Caixa::estrategia must return :estrategia verbatim by \
15094                 Copy — got {first:?}, expected {estrategia:?}",
15095            );
15096        }
15097        let c = caixa_with_estrategia(None);
15098        assert!(
15099            c.estrategia().is_none(),
15100            "Caixa::estrategia must return None when :estrategia is \
15101             absent — the author-omitted arm must project through the \
15102             accessor's Option::None unchanged",
15103        );
15104    }
15105
15106    // ── Caixa::max_restarts / Caixa::restart_window —
15107    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
15108    //    (Option<u32> / Option<&str>) folding on the ed04d3c
15109    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
15110
15111    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
15112        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15113        c.max_restarts = max_restarts;
15114        c
15115    }
15116
15117    fn caixa_supervisor_with_max_restarts_and_window(
15118        max_restarts: Option<u32>,
15119        restart_window: Option<&str>,
15120    ) -> Caixa {
15121        use crate::CaixaKind;
15122        use crate::supervisor::{ChildSpec, RestartPolicy};
15123        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
15124        c.kind = CaixaKind::Supervisor;
15125        c.max_restarts = max_restarts;
15126        c.restart_window = restart_window.map(str::to_string);
15127        c.children = vec![ChildSpec {
15128            caixa: "worker".into(),
15129            versao: "^0.1".into(),
15130            restart: RestartPolicy::Permanent,
15131        }];
15132        c
15133    }
15134
15135    #[test]
15136    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
15137        // Value-shape pin: [`Caixa::max_restarts`] returns the
15138        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
15139        // from the typed slot's own storage, byte-equal across the
15140        // author-omitted `None` arm (the "defer to the
15141        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
15142        // `{intensity, 5, 60}` default" partition every
15143        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
15144        // and each of the representative fixtures in the accept-set —
15145        // `0` (the zero-floor arm the peer
15146        // [`crate::supervisor::SupervisorSpec::validate`]
15147        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
15148        // the post-composition altitude — the accessor must ship the
15149        // raw slot verbatim so struct-literal fixtures continue to
15150        // expose the zero at the accessor boundary), the OTP-canonical
15151        // `5` default (`{intensity, 5, 60}` worker-supervisor from
15152        // Learn You Some Erlang), `1000` (the
15153        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
15154        // upper-bound gate accepts on the boundary), `u32::MAX` (a
15155        // past-the-cap sentinel that the substrate-primitive accessor
15156        // must still ship verbatim). Second outer top-level
15157        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
15158        // pin — folds on the sibling
15159        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
15160        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
15161        // onto the sibling `Option<u32>` restart-budget-count arm.
15162        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
15163        for max_restarts in fixtures {
15164            let c = caixa_with_max_restarts(max_restarts);
15165            assert_eq!(
15166                c.max_restarts(),
15167                max_restarts,
15168                "Caixa::max_restarts must return :max-restarts verbatim \
15169                 (got {:?}, expected {max_restarts:?})",
15170                c.max_restarts(),
15171            );
15172            assert_eq!(
15173                c.max_restarts(),
15174                c.max_restarts,
15175                "Caixa::max_restarts accessor and self.max_restarts \
15176                 field access must byte-equal — a presence-bit or count \
15177                 drift would silently split the paired \
15178                 Caixa::declared_supervisor_slots presence-probe arm \
15179                 from the Caixa::supervisor_view unwrap_or(5) fold's \
15180                 composition input",
15181            );
15182        }
15183    }
15184
15185    #[test]
15186    fn max_restarts_projects_option_by_copy() {
15187        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
15188        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
15189        // borrow `&self` past the call (no lifetime on the return type),
15190        // and calling the accessor twice on the same [`Caixa`] must
15191        // yield equal values (idempotent, no side effects). Peer of the
15192        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
15193        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
15194        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
15195            let c = caixa_with_max_restarts(max_restarts);
15196            let first = c.max_restarts();
15197            let second = c.max_restarts();
15198            assert_eq!(
15199                first, second,
15200                "Caixa::max_restarts must be idempotent — two successive \
15201                 calls on the same &self must return the same Option<u32>",
15202            );
15203            assert_eq!(
15204                first, max_restarts,
15205                "Caixa::max_restarts must return :max-restarts verbatim \
15206                 by Copy — got {first:?}, expected {max_restarts:?}",
15207            );
15208        }
15209    }
15210
15211    #[test]
15212    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
15213        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15214        // `:max-restarts` presence-probe arm must key off
15215        // [`Caixa::max_restarts`], not the raw
15216        // `self.max_restarts.is_some()` field-probe. Structurally: every
15217        // `Caixa { max_restarts: Some(_), .. }` variant must push
15218        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
15219        // list (the presence bit is `Some` for every representative
15220        // count, so the M2 kind-coherence gate must surface the slot as
15221        // "declared"), and a `Caixa { max_restarts: None, .. }` must
15222        // NOT push the label. Peer of the sibling
15223        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
15224        // (ed04d3c) composition pin — same routing-through-accessor
15225        // discipline extended onto the sibling flat-spread `Option<u32>`
15226        // arm.
15227        for max_restarts in [0u32, 5, 1000, u32::MAX] {
15228            let c = caixa_with_max_restarts(Some(max_restarts));
15229            let slots = c.declared_supervisor_slots();
15230            assert!(
15231                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15232                "declared_supervisor_slots must push \
15233                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
15234                 is Some({max_restarts}) — the accessor and the \
15235                 enumerator gate must route through the same \
15236                 substrate-primitive typed dispatch on the outer \
15237                 :max-restarts presence bit (got slots={slots:?})",
15238            );
15239        }
15240        let c = caixa_with_max_restarts(None);
15241        let slots = c.declared_supervisor_slots();
15242        assert!(
15243            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
15244            "declared_supervisor_slots must NOT push \
15245             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
15246             None — the author-omitted arm must route through the \
15247             accessor's None-return unchanged (got slots={slots:?})",
15248        );
15249    }
15250
15251    #[test]
15252    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
15253        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
15254        // [`SupervisorSpec`] construction arm must key off
15255        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
15256        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
15257        // every `:kind Supervisor` `Caixa` carrying an author-declared
15258        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
15259        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
15260        // carrying `None`, the composed [`SupervisorSpec`]'s
15261        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
15262        // of the sibling
15263        // `supervisor_view_estrategia_arm_routes_through_accessor`
15264        // (ed04d3c) composition pin.
15265        for max_restarts in [1u32, 5, 1000] {
15266            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
15267            let view = c.supervisor_view().expect(
15268                "supervisor_view must materialize a SupervisorSpec for a \
15269                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
15270            );
15271            assert_eq!(
15272                view.max_restarts(),
15273                max_restarts,
15274                "supervisor_view must carry the outer \
15275                 Caixa::max_restarts() Some arm onto the composed \
15276                 SupervisorSpec.max_restarts field verbatim (got {}, \
15277                 expected {max_restarts})",
15278                view.max_restarts(),
15279            );
15280        }
15281        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15282        let view = c.supervisor_view().expect(
15283            "supervisor_view must materialize a SupervisorSpec for a \
15284             :kind Supervisor Caixa carrying a None :max-restarts",
15285        );
15286        assert_eq!(
15287            view.max_restarts(),
15288            5,
15289            "supervisor_view must project the outer \
15290             Caixa::max_restarts() None arm onto the OTP-canonical \
15291             {{intensity, 5, 60}} default (5) through the flat-spread \
15292             unwrap_or(5) fold (got {})",
15293            view.max_restarts(),
15294        );
15295        assert!(
15296            c.max_restarts().is_none(),
15297            "Caixa::max_restarts() must remain None on the author-\
15298             omitted arm — the supervisor_view fold must not mutate \
15299             the outer flat-spread presence bit",
15300        );
15301    }
15302
15303    #[test]
15304    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
15305        // Value-shape pin: [`Caixa::restart_window`] returns the
15306        // `:restart-window` typed `Option<String>` verbatim as an
15307        // `Option<&str>`, borrowed from the typed slot's own storage,
15308        // byte-equal across the author-omitted `None` arm and each of
15309        // the representative fixtures in the accept-set — the canonical
15310        // `"60s"` from `{intensity, 5, 60}`, the sibling
15311        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
15312        // / `"0s"`) the shared codec's positive-set sweep pin covers,
15313        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
15314        // seconds drift the sibling [`Self::validate_restart_window`]
15315        // gate refuses; the accessor must ship the raw slot verbatim
15316        // so struct-literal fixtures continue to expose the drift at
15317        // the accessor boundary). Third outer top-level [`Caixa`]
15318        // supervisor-tree flat-spread pin — extends the sub-family onto
15319        // the sibling `Option<&str>` raw-duration-string arm.
15320        for window in [
15321            None,
15322            Some("60s"),
15323            Some("5m"),
15324            Some("1h"),
15325            Some("500ms"),
15326            Some("1.5s"),
15327            Some(""),
15328        ] {
15329            let c = caixa_with_restart_window(window);
15330            assert_eq!(
15331                c.restart_window(),
15332                window,
15333                "Caixa::restart_window must return :restart-window \
15334                 verbatim as Option<&str> (got {:?}, expected {window:?})",
15335                c.restart_window(),
15336            );
15337            assert_eq!(
15338                c.restart_window(),
15339                c.restart_window.as_deref(),
15340                "Caixa::restart_window accessor and \
15341                 self.restart_window.as_deref() field access must \
15342                 byte-equal — a byte-level drift would silently split \
15343                 the paired Caixa::declared_supervisor_slots \
15344                 presence-probe arm from the \
15345                 Caixa::validate_restart_window shared-codec gate and \
15346                 the Caixa::supervisor_view soft-swallowing fold",
15347            );
15348        }
15349    }
15350
15351    #[test]
15352    fn restart_window_projects_slice_by_borrow() {
15353        // The by-borrow pin: [`Caixa::restart_window`] returns
15354        // `Option<&str>` by borrow — the returned string slice borrows
15355        // the underlying `Option<String>` storage of the `:restart-window`
15356        // slot and the accessor must not clone on every call. Peer of
15357        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
15358        // by-borrow pins on the universal-axis scalar family
15359        // (`licenca_projects_option_ref_by_borrow` /
15360        // `descricao_projects_option_ref_by_borrow` and siblings) —
15361        // extended onto the M2 supervisor-tree flat-spread
15362        // `Option<&str>` raw-duration-string axis.
15363        for window in [None, Some("60s"), Some("5m"), Some("")] {
15364            let c = caixa_with_restart_window(window);
15365            let first = c.restart_window();
15366            let second = c.restart_window();
15367            assert_eq!(
15368                first, second,
15369                "Caixa::restart_window must be idempotent — two \
15370                 successive calls on the same &self must return the \
15371                 same Option<&str>",
15372            );
15373            if let (Some(a), Some(b)) = (first, second) {
15374                assert_eq!(
15375                    a.as_ptr(),
15376                    b.as_ptr(),
15377                    "Caixa::restart_window must borrow the underlying \
15378                     String storage — two successive Some-arm calls must \
15379                     return slices with the same backing pointer (a fresh \
15380                     String clone would change the pointer on every call)",
15381                );
15382            }
15383            assert_eq!(
15384                first, window,
15385                "Caixa::restart_window must return :restart-window \
15386                 verbatim by borrow — got {first:?}, expected {window:?}",
15387            );
15388        }
15389    }
15390
15391    #[test]
15392    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
15393        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15394        // `:restart-window` presence-probe arm must key off
15395        // [`Caixa::restart_window`], not the raw
15396        // `self.restart_window.is_some()` field-probe. Structurally:
15397        // every `Caixa { restart_window: Some(_), .. }` must push
15398        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
15399        // list, and a `Caixa { restart_window: None, .. }` must NOT
15400        // push the label. Peer of the sibling
15401        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
15402        // routing pin.
15403        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
15404            let c = caixa_with_restart_window(Some(window));
15405            let slots = c.declared_supervisor_slots();
15406            assert!(
15407                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15408                "declared_supervisor_slots must push \
15409                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
15410                 `:restart-window` is Some({window:?}) — the accessor \
15411                 and the enumerator gate must route through the same \
15412                 substrate-primitive typed dispatch on the outer \
15413                 :restart-window presence bit (got slots={slots:?})",
15414            );
15415        }
15416        let c = caixa_with_restart_window(None);
15417        let slots = c.declared_supervisor_slots();
15418        assert!(
15419            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
15420            "declared_supervisor_slots must NOT push \
15421             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
15422             is None — the author-omitted arm must route through the \
15423             accessor's None-return unchanged (got slots={slots:?})",
15424        );
15425    }
15426
15427    #[test]
15428    fn validate_restart_window_arm_routes_through_accessor() {
15429        // Composition pin: [`Caixa::validate_restart_window`]'s
15430        // shared-codec fold arm must key off [`Caixa::restart_window`],
15431        // not the raw `self.restart_window.as_deref()` field-projection.
15432        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
15433        // express no reset" canonical shape); (2) a canonical `Some`
15434        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
15435        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
15436        // .. })` carrying the offending raw string verbatim. The three
15437        // arms jointly pin that the validator's raw-string binding is
15438        // the accessor's return, not a peer projection — any future
15439        // silent detour that had the accessor collapse `Some("")` to
15440        // `None` would silently absorb the empty-after-trim refusal
15441        // case at the accessor boundary.
15442        caixa_with_restart_window(None)
15443            .validate_restart_window()
15444            .expect("None :restart-window must validate through the accessor");
15445        caixa_with_restart_window(Some("60s"))
15446            .validate_restart_window()
15447            .expect("canonical :restart-window \"60s\" must validate through the accessor");
15448        let err = caixa_with_restart_window(Some("1.5s"))
15449            .validate_restart_window()
15450            .expect_err("fractional-seconds :restart-window must fail through the accessor");
15451        assert!(
15452            matches!(
15453                err,
15454                ManifestError::RestartWindowMalformed { ref restart_window, .. }
15455                    if restart_window == "1.5s"
15456            ),
15457            "validator must carry the offending raw string verbatim \
15458             from the accessor's borrowed &str (got {err:?})",
15459        );
15460    }
15461
15462    #[test]
15463    fn supervisor_view_restart_window_arm_routes_through_accessor() {
15464        // Composition pin: [`Caixa::supervisor_view`]'s
15465        // per-`:restart-window` [`SupervisorSpec`] construction arm
15466        // must key off [`Caixa::restart_window`]'s soft-swallowing
15467        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
15468        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
15469        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
15470        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
15471        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
15472        // (the shared codec's canonical parse); (3) codec-rejected
15473        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
15474        // (the soft-swallow preserving the view's best-effort shape).
15475        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
15476        let view = c.supervisor_view().expect("Supervisor kind has a view");
15477        assert_eq!(
15478            view.restart_window(),
15479            None,
15480            "supervisor_view must project outer None :restart-window \
15481             onto None on the composed SupervisorSpec (never-reset \
15482             sentinel) through the accessor's None-return unchanged",
15483        );
15484
15485        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
15486        let view = c.supervisor_view().expect("Supervisor kind has a view");
15487        assert_eq!(
15488            view.restart_window(),
15489            Some(std::time::Duration::from_secs(60)),
15490            "supervisor_view must fold outer Some(\"60s\") through the \
15491             shared duration_codec into Duration::from_secs(60) on the \
15492             composed SupervisorSpec (accessor's Some(&str) → codec \
15493             parse → Some(Duration))",
15494        );
15495
15496        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
15497        let view = c.supervisor_view().expect("Supervisor kind has a view");
15498        assert_eq!(
15499            view.restart_window(),
15500            None,
15501            "supervisor_view must soft-swallow the shared-codec parse \
15502             failure to None (the view's best-effort shape the sibling \
15503             manifest-level validate_restart_window surfaces as \
15504             RestartWindowMalformed); the accessor's raw-string return \
15505             is the single input every downstream consumer keys off",
15506        );
15507    }
15508
15509    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
15510
15511    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
15512        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15513        c.upgrade_from = upgrade_from;
15514        c
15515    }
15516
15517    #[test]
15518    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
15519        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
15520        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
15521        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
15522        // typed `Vec<UpgradeFromEntry>` verbatim as a
15523        // `&[UpgradeFromEntry]` slice-view over the same backing
15524        // buffer the raw `self.upgrade_from.as_slice()` field access
15525        // borrows from, element-equal across every representative
15526        // fixture in the accept-set — `[]` (the "no hot-upgrade path
15527        // declared" arm every `defcaixa` without an `:upgrade-from`
15528        // block carries; `#[serde(default)]` folds an omitted slot
15529        // onto `Vec::new()`), a canonical single-entry `Restart`
15530        // fixture (the shape most Servicos carry — a single prior
15531        // version with the fallback strategy), a canonical multi-
15532        // entry list carrying every typed instruction variant
15533        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
15534        // `Restart`), and a past-the-guard sentinel — a duplicate-
15535        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
15536        // ([`crate::upgrade::validate_upgrade_from`] rejects through
15537        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
15538        // ship the raw slot verbatim so struct-literal fixtures
15539        // continue to expose the duplicate at the accessor boundary).
15540        //
15541        // Pins against a future silent detour that returned an owned
15542        // `Vec<UpgradeFromEntry>` (which would type-check but silently
15543        // clone on every accessor call, breaking the zero-cost
15544        // projection every peer sibling slice accessor carries), a
15545        // `[dup, dup] → [dup]` dedup collapse (which would silently
15546        // absorb the `DuplicateFrom` refusal case at the accessor
15547        // boundary and the [`crate::StandardLayout::verify`] cross-
15548        // entry gate would silently accept a struct-literal `Caixa`
15549        // carrying the drift), a reference to an operator-resolved
15550        // overlay (the future per-cluster `:upgrade-overrides` slot
15551        // — its resolution must land at exactly this accessor body,
15552        // not silently divert the raw slot away from a second
15553        // consumer), or an axis-shuffled projection (a future detour
15554        // that reordered entries through the accessor would silently
15555        // split the paired [`crate::StandardLayout::verify`] per-
15556        // `:upgrade-from` shape gate's traversal input from the peer
15557        // [`crate::render::servico_m2_overlay`] emitter's projection
15558        // input, since the operator's hot-upgrade dispatch matches
15559        // per-`:from` and axis reordering would silently split the
15560        // per-entry script-path existence probe's iteration order
15561        // from the M2 overlay emitter's serialized-entry order).
15562        //
15563        // First outer top-level [`Caixa`] `&[Composite]`-return
15564        // slice accessor pin on the substrate primitive for M2 / M3
15565        // typed-slot vec-carry axes — opens the outer-`Caixa`
15566        // `&[Composite]` composite-slice projection pattern the
15567        // sibling `:children` [`crate::supervisor::ChildSpec`] /
15568        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
15569        // [`crate::aplicacao::WitContract`] future outer-composite-
15570        // slice pins fold on. Peer of the closed outer-`Caixa`
15571        // scalar `Option<&Composite>` composite-reference family the
15572        // sibling `limits` / `behavior` / `politicas` / `placement`
15573        // / `entrada` `..._returns_..._option_ref_verbatim_across_
15574        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
15575        // the "byte-equal, borrow-shared" outer-accessor discipline
15576        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
15577        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15578        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
15579            vec![],
15580            vec![UpgradeFromEntry {
15581                from: "0.0.1".into(),
15582                instructions: vec![UpgradeInstruction::Restart],
15583            }],
15584            vec![
15585                UpgradeFromEntry {
15586                    from: "0.0.1".into(),
15587                    instructions: vec![
15588                        UpgradeInstruction::LoadModule {
15589                            module: "demo".into(),
15590                        },
15591                        UpgradeInstruction::SoftPurge {
15592                            module: "demo".into(),
15593                        },
15594                    ],
15595                },
15596                UpgradeFromEntry {
15597                    from: "0.0.2".into(),
15598                    instructions: vec![
15599                        UpgradeInstruction::StateChange {
15600                            script: "servicos/upgrade.lisp".into(),
15601                        },
15602                        UpgradeInstruction::Purge {
15603                            module: "demo".into(),
15604                        },
15605                        UpgradeInstruction::Restart,
15606                    ],
15607                },
15608            ],
15609            vec![
15610                UpgradeFromEntry {
15611                    from: "0.1.0".into(),
15612                    instructions: vec![UpgradeInstruction::Restart],
15613                },
15614                UpgradeFromEntry {
15615                    from: "0.1.0".into(),
15616                    instructions: vec![UpgradeInstruction::Restart],
15617                },
15618            ],
15619        ];
15620        for upgrade_from in fixtures {
15621            let c = caixa_with_upgrade_from(upgrade_from.clone());
15622            assert_eq!(
15623                c.upgrade_from(),
15624                upgrade_from.as_slice(),
15625                "Caixa::upgrade_from must return :upgrade-from \
15626                 verbatim (got {:?}, expected {upgrade_from:?})",
15627                c.upgrade_from(),
15628            );
15629            assert_eq!(
15630                c.upgrade_from(),
15631                c.upgrade_from.as_slice(),
15632                "Caixa::upgrade_from must element-equal the raw \
15633                 `self.upgrade_from.as_slice()` field access across \
15634                 every value in the Vec<UpgradeFromEntry> accept-set",
15635            );
15636            assert_eq!(
15637                c.upgrade_from().is_empty(),
15638                c.upgrade_from.is_empty(),
15639                "Caixa::upgrade_from().is_empty() must byte-equal \
15640                 self.upgrade_from.is_empty() — a presence-bit drift \
15641                 would silently split the paired \
15642                 Caixa::declared_servico_slots M2 declared-slot \
15643                 enumerator's presence probe from the peer \
15644                 crate::render::servico_m2_overlay M2 overlay \
15645                 emitter's presence gate",
15646            );
15647        }
15648    }
15649
15650    #[test]
15651    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
15652        // Composition pin: [`Caixa::declared_servico_slots`]'s
15653        // `:upgrade-from` presence-probe arm must key off
15654        // [`Caixa::upgrade_from`], not the raw
15655        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
15656        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
15657        // instructions: vec![Restart] }], .. }` must push
15658        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
15659        // (the presence bit is non-empty, so the M2 kind-coherence
15660        // gate must surface the slot as "declared"), and a `Caixa {
15661        // upgrade_from: vec![], .. }` must NOT push the label (the
15662        // "author omitted the slot entirely" arm — the empty-slice
15663        // partition the serde-default folds onto). The pair jointly
15664        // pins the accessor + declared-slot enumerator composition:
15665        // any future silent detour that had the accessor collapse
15666        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
15667        // is_empty())` projection) would silently absorb the
15668        // "declared but degenerate" arm at the accessor boundary and
15669        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
15670        // coherence gate would silently accept a struct-literal
15671        // `Caixa` carrying the drift.
15672        //
15673        // Peer of the sibling
15674        // `declared_servico_slots_limits_arm_routes_through_accessor`
15675        // (b2bd9d7) and
15676        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15677        // (35d8b52) composition pins on the sibling `:limits` /
15678        // `:behavior` outer-`Option<&Composite>` arms — same "the
15679        // enumerator gate must route through the substrate-primitive
15680        // typed dispatch" discipline extended onto the third M2
15681        // Servico-runtime slot axis, closing the enumerator's routing
15682        // invariant on every M2 arm.
15683        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15684        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15685            from: "0.0.1".into(),
15686            instructions: vec![UpgradeInstruction::Restart],
15687        }]);
15688        let slots = c.declared_servico_slots();
15689        assert!(
15690            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15691            "declared_servico_slots must push \
15692             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15693             non-empty — the accessor and the enumerator gate must \
15694             route through the same substrate-primitive typed \
15695             dispatch on the outer :upgrade-from presence bit (got \
15696             slots={slots:?})",
15697        );
15698        let c = caixa_with_upgrade_from(vec![]);
15699        let slots = c.declared_servico_slots();
15700        assert!(
15701            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
15702            "declared_servico_slots must NOT push \
15703             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
15704             empty — the author-omitted arm must route through the \
15705             accessor's empty-slice return unchanged (got \
15706             slots={slots:?})",
15707        );
15708    }
15709
15710    #[test]
15711    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
15712        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15713        // per-`:upgrade-from` M2 overlay emit arm must key off
15714        // [`Caixa::upgrade_from`], not the raw
15715        // `!caixa.upgrade_from.is_empty()` presence gate + the
15716        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
15717        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
15718        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
15719        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
15720        // sequence in the overlay (the emitter fans onto the serde
15721        // slice-serialization), and a `Caixa { upgrade_from: vec![],
15722        // .. }` must omit the key entirely (the empty-slice
15723        // partition — the `!.is_empty()` outer gate elides the key
15724        // when the author omitted the slot). The pair jointly pins
15725        // the accessor + M2 overlay emitter composition: any future
15726        // silent detour that had the accessor return a fresh-cloned
15727        // `Vec<UpgradeFromEntry>` copy would silently break the
15728        // reference-identity pin the peer per-entry
15729        // `serde_yaml::to_value(caixa.upgrade_from())` projection
15730        // reads from — the projection would clone once per accessor
15731        // call instead of borrowing the storage buffer verbatim.
15732        //
15733        // Peer of the sibling
15734        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15735        // (b2bd9d7) and
15736        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
15737        // (35d8b52) composition pins on the sibling `:limits` /
15738        // `:behavior` outer-`Option<&Composite>` arms — same "the
15739        // M2 overlay emitter must route through the substrate-
15740        // primitive typed dispatch" discipline extended onto the
15741        // third M2 Servico-runtime slot axis, closing the overlay
15742        // emitter's routing invariant on every M2 arm.
15743        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
15744        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15745        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
15746            from: "0.0.1".into(),
15747            instructions: vec![UpgradeInstruction::Restart],
15748        }]);
15749        let overlay = servico_m2_overlay(&c).unwrap();
15750        assert!(
15751            overlay.contains_key(M2_KEY_UPGRADE_FROM),
15752            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
15753             `:upgrade-from` is non-empty — the accessor and the M2 \
15754             overlay emitter must route through the same substrate- \
15755             primitive typed dispatch on the outer :upgrade-from \
15756             slice (got overlay={overlay:?})",
15757        );
15758        let c = caixa_with_upgrade_from(vec![]);
15759        let overlay = servico_m2_overlay(&c).unwrap();
15760        assert!(
15761            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
15762            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
15763             `:upgrade-from` is empty — the empty-slice partition \
15764             must route through the accessor's empty-slice return \
15765             unchanged (got overlay={overlay:?})",
15766        );
15767    }
15768
15769    #[test]
15770    fn upgrade_from_projects_slice_by_borrow() {
15771        // The by-borrow pin: [`Caixa::upgrade_from`] returns
15772        // `&[UpgradeFromEntry]` by borrow — the returned slice
15773        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
15774        // the `:upgrade-from` slot and the accessor must not clone
15775        // the backing `Vec` on every call. Peer of the sibling
15776        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
15777        // (`autores_projects_slice_by_borrow` b5d813f,
15778        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
15779        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
15780        // `exe_projects_slice_by_borrow` 65d9527,
15781        // `servicos_projects_slice_by_borrow` 611f78b,
15782        // `deps_projects_slice_by_borrow` ad34b4e,
15783        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
15784        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
15785        // axes — extended here to the first outer-`Caixa`
15786        // composite-element `&[Composite]` axis: the accessor's
15787        // returned slice must borrow from `&self` (the returned
15788        // reference's lifetime is tied to `&self`), and calling the
15789        // accessor twice on the same [`Caixa`] must yield slices
15790        // that are pointer-equal (the underlying byte-buffer is the
15791        // storage `Vec`'s allocation, not a fresh copy) as well as
15792        // value-equal (idempotent, no side effects on `&self`).
15793        //
15794        // Pins against a future silent detour that returned an owned
15795        // `Vec<UpgradeFromEntry>` (which would type-check but
15796        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
15797        // return (which would leak the backing `Vec`'s
15798        // grow/push/reserve surface no downstream consumer reaches
15799        // for), or a one-arm-only accessor that returned a
15800        // saturating value on some sentinel input.
15801        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
15802        for upgrade_from in [
15803            vec![],
15804            vec![UpgradeFromEntry {
15805                from: "0.0.1".into(),
15806                instructions: vec![UpgradeInstruction::Restart],
15807            }],
15808            vec![
15809                UpgradeFromEntry {
15810                    from: "0.0.1".into(),
15811                    instructions: vec![UpgradeInstruction::Restart],
15812                },
15813                UpgradeFromEntry {
15814                    from: "0.0.2".into(),
15815                    instructions: vec![UpgradeInstruction::SoftPurge {
15816                        module: "demo".into(),
15817                    }],
15818                },
15819            ],
15820        ] {
15821            let c = caixa_with_upgrade_from(upgrade_from.clone());
15822            let first = c.upgrade_from();
15823            let second = c.upgrade_from();
15824            assert_eq!(
15825                first, second,
15826                "Caixa::upgrade_from must be idempotent — two \
15827                 successive calls on the same &self must return the \
15828                 same &[UpgradeFromEntry]",
15829            );
15830            assert_eq!(
15831                first.as_ptr(),
15832                second.as_ptr(),
15833                "Caixa::upgrade_from must borrow the underlying \
15834                 Vec<UpgradeFromEntry> storage — two successive calls \
15835                 must return slices with the same backing pointer (a \
15836                 fresh Vec<UpgradeFromEntry> clone would change the \
15837                 pointer on every call)",
15838            );
15839            assert_eq!(
15840                first,
15841                upgrade_from.as_slice(),
15842                "Caixa::upgrade_from must return :upgrade-from \
15843                 verbatim by borrow — got {first:?}, expected \
15844                 {upgrade_from:?}",
15845            );
15846        }
15847    }
15848
15849    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
15850
15851    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
15852        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15853        c.children = children;
15854        c
15855    }
15856
15857    #[test]
15858    fn children_returns_children_slice_verbatim_across_permutations() {
15859        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
15860        // outer-composite `&[ChildSpec]`-return slice-shape pin:
15861        // [`Caixa::children`] must return the `:children` typed
15862        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
15863        // the same backing buffer the raw `self.children.as_slice()`
15864        // field access borrows from, element-equal across every
15865        // representative fixture in the accept-set — `[]` (the "no
15866        // static children declared" arm every non-`Supervisor`-kind
15867        // `defcaixa` carries by `#[serde(default)]` and every
15868        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
15869        // a canonical single-child `Permanent` fixture (the shape
15870        // most `OneForOne` supervisors carry — a single long-running
15871        // worker child), a canonical multi-child list carrying every
15872        // typed restart-policy variant (`Permanent` / `Transient` /
15873        // `Temporary`), and a past-the-guard sentinel — a duplicate
15874        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
15875        // ([`crate::SupervisorSpec::validate`] rejects through
15876        // `DuplicateChildNome { nome: "w" }` but the accessor must
15877        // ship the raw slot verbatim so struct-literal fixtures
15878        // continue to expose the duplicate at the accessor boundary).
15879        //
15880        // Pins against a future silent detour that returned an owned
15881        // `Vec<ChildSpec>` (which would type-check but silently clone
15882        // on every accessor call, breaking the zero-cost projection
15883        // every peer sibling slice accessor carries), a `[dup, dup] →
15884        // [dup]` dedup collapse (which would silently absorb the
15885        // `DuplicateChildNome` refusal case at the accessor boundary
15886        // and the [`crate::StandardLayout::verify`] cross-child gate
15887        // would silently accept a struct-literal `Caixa` carrying the
15888        // drift), a reference to an operator-resolved overlay (the
15889        // future per-cluster `:children-overrides` slot — its
15890        // resolution must land at exactly this accessor body, not
15891        // silently divert the raw slot away from a second consumer),
15892        // or an axis-shuffled projection (a future detour that
15893        // reordered children through the accessor would silently
15894        // split the paired [`crate::StandardLayout::verify`] per-
15895        // supervisor gate's traversal input from the peer
15896        // [`Self::supervisor_view`] fold-in path's clone-order input,
15897        // since the OTP `RestForOne` restart strategy dispatches on
15898        // declared child order and axis reordering would silently
15899        // split the operator's per-cluster restart-fan-out order
15900        // from the caixa.lisp source-order).
15901        //
15902        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
15903        // accessor pin on the substrate primitive for M2 / M3 typed-
15904        // slot vec-carry axes — folds on the outer-`Caixa`
15905        // `&[Composite]` composite-slice sub-family the sibling
15906        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
15907        // (2a1f907) pin opened, peer at the outer altitude of the
15908        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
15909        // (bc92bce) accessor on the same OTP-supervisor static-child-
15910        // list axis.
15911        use crate::supervisor::{ChildSpec, RestartPolicy};
15912        let fixtures: Vec<Vec<ChildSpec>> = vec![
15913            vec![],
15914            vec![ChildSpec {
15915                caixa: "worker".into(),
15916                versao: "^0.1".into(),
15917                restart: RestartPolicy::Permanent,
15918            }],
15919            vec![
15920                ChildSpec {
15921                    caixa: "worker-a".into(),
15922                    versao: "^0.1".into(),
15923                    restart: RestartPolicy::Permanent,
15924                },
15925                ChildSpec {
15926                    caixa: "worker-b".into(),
15927                    versao: "^0.1".into(),
15928                    restart: RestartPolicy::Transient,
15929                },
15930                ChildSpec {
15931                    caixa: "worker-c".into(),
15932                    versao: "^0.1".into(),
15933                    restart: RestartPolicy::Temporary,
15934                },
15935            ],
15936            vec![
15937                ChildSpec {
15938                    caixa: "w".into(),
15939                    versao: "^0.1".into(),
15940                    restart: RestartPolicy::Permanent,
15941                },
15942                ChildSpec {
15943                    caixa: "w".into(),
15944                    versao: "^0.1".into(),
15945                    restart: RestartPolicy::Permanent,
15946                },
15947            ],
15948        ];
15949        for children in fixtures {
15950            let c = caixa_with_children(children.clone());
15951            assert_eq!(
15952                c.children(),
15953                children.as_slice(),
15954                "Caixa::children must return :children verbatim \
15955                 (got {:?}, expected {children:?})",
15956                c.children(),
15957            );
15958            assert_eq!(
15959                c.children(),
15960                c.children.as_slice(),
15961                "Caixa::children must element-equal the raw \
15962                 `self.children.as_slice()` field access across \
15963                 every value in the Vec<ChildSpec> accept-set",
15964            );
15965            assert_eq!(
15966                c.children().is_empty(),
15967                c.children.is_empty(),
15968                "Caixa::children().is_empty() must byte-equal \
15969                 self.children.is_empty() — a presence-bit drift \
15970                 would silently split the paired \
15971                 Caixa::declared_supervisor_slots supervisor-tree \
15972                 declared-slot enumerator's presence probe from the \
15973                 peer Caixa::supervisor_view typed-view composer's \
15974                 fold-in path",
15975            );
15976        }
15977    }
15978
15979    #[test]
15980    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
15981        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
15982        // `:children` presence-probe arm must key off
15983        // [`Caixa::children`], not the raw
15984        // `!self.children.is_empty()` field-probe. Structurally: a
15985        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
15986        // "^0.1", restart: Permanent }], .. }` must push
15987        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
15988        // (the presence bit is non-empty, so the supervisor-tree
15989        // kind-coherence gate must surface the slot as "declared"),
15990        // and a `Caixa { children: vec![], .. }` must NOT push the
15991        // label (the "author omitted the slot entirely" arm — the
15992        // empty-slice partition the serde-default folds onto). The
15993        // pair jointly pins the accessor + declared-slot enumerator
15994        // composition: any future silent detour that had the accessor
15995        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
15996        // "__reserved__")` projection) would silently absorb the
15997        // "declared but degenerate" arm at the accessor boundary and
15998        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
15999        // kind-coherence gate would silently accept a struct-literal
16000        // `Caixa` carrying the drift.
16001        //
16002        // Peer of the sibling
16003        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16004        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
16005        // same "the enumerator gate must route through the substrate-
16006        // primitive typed dispatch" discipline extended onto the
16007        // supervisor-tree `:children` composite-slice arm.
16008        use crate::supervisor::{ChildSpec, RestartPolicy};
16009        let c = caixa_with_children(vec![ChildSpec {
16010            caixa: "w".into(),
16011            versao: "^0.1".into(),
16012            restart: RestartPolicy::Permanent,
16013        }]);
16014        let slots = c.declared_supervisor_slots();
16015        assert!(
16016            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16017            "declared_supervisor_slots must push \
16018             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
16019             non-empty — the accessor and the enumerator gate must \
16020             route through the same substrate-primitive typed \
16021             dispatch on the outer :children presence bit (got \
16022             slots={slots:?})",
16023        );
16024        let c = caixa_with_children(vec![]);
16025        let slots = c.declared_supervisor_slots();
16026        assert!(
16027            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
16028            "declared_supervisor_slots must NOT push \
16029             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
16030             empty — the author-omitted arm must route through the \
16031             accessor's empty-slice return unchanged (got \
16032             slots={slots:?})",
16033        );
16034    }
16035
16036    #[test]
16037    fn supervisor_view_children_arm_routes_through_accessor() {
16038        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
16039        // fold-in arm must key off [`Caixa::children`], not the raw
16040        // `self.children.clone()` field-clone. Structurally: a `Caixa {
16041        // kind: Supervisor, estrategia: Some(OneForOne), children:
16042        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
16043        // per-child list through the accessor into the typed
16044        // [`SupervisorSpec`] view's `children` field verbatim — every
16045        // entry the accessor surfaces must land in the view's
16046        // `children` slot in the same order. The pair jointly pins the
16047        // accessor + view-composer composition: any future silent
16048        // detour that had the accessor return a fresh-cloned
16049        // `Vec<ChildSpec>` copy would silently break the reference-
16050        // identity pin the peer `supervisor_view` fold-in path reads
16051        // from — the fold would clone once more per accessor call
16052        // instead of borrowing the storage buffer verbatim once.
16053        //
16054        // Peer of the sibling
16055        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
16056        // family) composition pin on the peer kind-gate arm — same
16057        // "the view composer must route through the substrate-
16058        // primitive typed dispatch" discipline extended onto the
16059        // per-`:children` fold-in arm, closing the supervisor-view
16060        // composer's routing invariant on the composite-slice input.
16061        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16062        let mut c = caixa_with_children(vec![
16063            ChildSpec {
16064                caixa: "worker-a".into(),
16065                versao: "^0.1".into(),
16066                restart: RestartPolicy::Permanent,
16067            },
16068            ChildSpec {
16069                caixa: "worker-b".into(),
16070                versao: "^0.1".into(),
16071                restart: RestartPolicy::Transient,
16072            },
16073        ]);
16074        c.kind = crate::CaixaKind::Supervisor;
16075        c.estrategia = Some(RestartStrategy::OneForOne);
16076        let view = c
16077            .supervisor_view()
16078            .expect("Supervisor kind must produce a supervisor_view");
16079        assert_eq!(
16080            view.children(),
16081            c.children(),
16082            "supervisor_view must fold Caixa::children verbatim into \
16083             SupervisorSpec::children — the accessor and the view \
16084             composer must route through the same substrate-primitive \
16085             typed dispatch on the outer :children slice (got view \
16086             children={:?}, expected {:?})",
16087            view.children(),
16088            c.children(),
16089        );
16090    }
16091
16092    #[test]
16093    fn children_projects_slice_by_borrow() {
16094        // The by-borrow pin: [`Caixa::children`] returns
16095        // `&[ChildSpec]` by borrow — the returned slice borrows the
16096        // underlying `Vec<ChildSpec>` storage of the `:children` slot
16097        // and the accessor must not clone the backing `Vec` on every
16098        // call. Peer of the sibling outer top-level [`Caixa`]
16099        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
16100        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
16101        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16102        // `exe_projects_slice_by_borrow` 65d9527,
16103        // `servicos_projects_slice_by_borrow` 611f78b,
16104        // `deps_projects_slice_by_borrow` ad34b4e,
16105        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16106        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
16107        // sibling outer top-level [`Caixa`] scalar-element and
16108        // composite-element `&[T]` axes — folds on the outer-`Caixa`
16109        // composite-element `&[Composite]` axis: the accessor's
16110        // returned slice must borrow from `&self` (the returned
16111        // reference's lifetime is tied to `&self`), and calling the
16112        // accessor twice on the same [`Caixa`] must yield slices
16113        // that are pointer-equal (the underlying byte-buffer is the
16114        // storage `Vec`'s allocation, not a fresh copy) as well as
16115        // value-equal (idempotent, no side effects on `&self`).
16116        //
16117        // Pins against a future silent detour that returned an owned
16118        // `Vec<ChildSpec>` (which would type-check but silently clone
16119        // on every call), a `&Vec<ChildSpec>` return (which would leak
16120        // the backing `Vec`'s grow/push/reserve surface no downstream
16121        // consumer reaches for), or a one-arm-only accessor that
16122        // returned a saturating value on some sentinel input.
16123        use crate::supervisor::{ChildSpec, RestartPolicy};
16124        for children in [
16125            vec![],
16126            vec![ChildSpec {
16127                caixa: "w".into(),
16128                versao: "^0.1".into(),
16129                restart: RestartPolicy::Permanent,
16130            }],
16131            vec![
16132                ChildSpec {
16133                    caixa: "worker-a".into(),
16134                    versao: "^0.1".into(),
16135                    restart: RestartPolicy::Permanent,
16136                },
16137                ChildSpec {
16138                    caixa: "worker-b".into(),
16139                    versao: "^0.1".into(),
16140                    restart: RestartPolicy::Transient,
16141                },
16142            ],
16143        ] {
16144            let c = caixa_with_children(children.clone());
16145            let first = c.children();
16146            let second = c.children();
16147            assert_eq!(
16148                first, second,
16149                "Caixa::children must be idempotent — two successive \
16150                 calls on the same &self must return the same \
16151                 &[ChildSpec]",
16152            );
16153            assert_eq!(
16154                first.as_ptr(),
16155                second.as_ptr(),
16156                "Caixa::children must borrow the underlying \
16157                 Vec<ChildSpec> storage — two successive calls must \
16158                 return slices with the same backing pointer (a fresh \
16159                 Vec<ChildSpec> clone would change the pointer on \
16160                 every call)",
16161            );
16162            assert_eq!(
16163                first,
16164                children.as_slice(),
16165                "Caixa::children must return :children verbatim by \
16166                 borrow — got {first:?}, expected {children:?}",
16167            );
16168        }
16169    }
16170
16171    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
16172
16173    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
16174        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16175        c.kind = CaixaKind::Aplicacao;
16176        c.membros = membros;
16177        c
16178    }
16179
16180    #[test]
16181    fn membros_returns_membros_slice_verbatim_across_permutations() {
16182        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
16183        // composite `&[Membro]`-return slice-shape pin:
16184        // [`Caixa::membros`] must return the `:membros` typed
16185        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
16186        // same backing buffer the raw `self.membros.as_slice()` field
16187        // access borrows from, element-equal across every
16188        // representative fixture in the accept-set — `[]` (the "no
16189        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
16190        // carries by `#[serde(default)]` and every partially-authored
16191        // Aplicacao carries before the
16192        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
16193        // canonical single-member fixture (the shape a minimal
16194        // Aplicacao carries — one Servico wrapping one contained
16195        // computation), a canonical multi-member list carrying three
16196        // distinct entries (the canonical checkout-shape Aplicacao —
16197        // cart / pricing / auth — every canonical example carries), and
16198        // a past-the-guard sentinel — a duplicate `:caixa`
16199        // `[("cart", ...), ("cart", ...)]` entry pair
16200        // ([`crate::AplicacaoSpec::validate`] rejects through
16201        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
16202        // the raw slot verbatim so struct-literal fixtures continue to
16203        // expose the duplicate at the accessor boundary).
16204        //
16205        // Pins against a future silent detour that returned an owned
16206        // `Vec<Membro>` (which would type-check but silently clone on
16207        // every accessor call, breaking the zero-cost projection every
16208        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
16209        // dedup collapse (which would silently absorb the
16210        // `DuplicateMembro` refusal case at the accessor boundary and
16211        // the [`crate::StandardLayout::verify`] cross-member gate would
16212        // silently accept a struct-literal `Caixa` carrying the drift),
16213        // a reference to an operator-resolved overlay (the future per-
16214        // cluster `:membros-overrides` slot — its resolution must land
16215        // at exactly this accessor body, not silently divert the raw
16216        // slot away from a second consumer), or an axis-shuffled
16217        // projection (a future detour that reordered members through
16218        // the accessor would silently split the paired
16219        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16220        // traversal input from the peer [`Self::aplicacao_view`] fold-
16221        // in path's clone-order input, since the canonical `:contratos`
16222        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
16223        // read the member set through the same slice).
16224        //
16225        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
16226        // accessor pin on the substrate primitive for M2 / M3 typed-
16227        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
16228        // arm of the `&[Composite]` composite-slice sub-family the
16229        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16230        // (2a1f907) and
16231        // `children_returns_children_slice_verbatim_across_permutations`
16232        // (c17b51e) pins opened, peer at the outer altitude of the
16233        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
16234        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
16235        // list axis.
16236        use crate::aplicacao::Membro;
16237        let fixtures: Vec<Vec<Membro>> = vec![
16238            vec![],
16239            vec![Membro {
16240                caixa: "cart".into(),
16241                versao: "^0.1".into(),
16242            }],
16243            vec![
16244                Membro {
16245                    caixa: "cart".into(),
16246                    versao: "^0.1".into(),
16247                },
16248                Membro {
16249                    caixa: "pricing".into(),
16250                    versao: "^0.2".into(),
16251                },
16252                Membro {
16253                    caixa: "auth".into(),
16254                    versao: "^1.0".into(),
16255                },
16256            ],
16257            vec![
16258                Membro {
16259                    caixa: "cart".into(),
16260                    versao: "^0.1".into(),
16261                },
16262                Membro {
16263                    caixa: "cart".into(),
16264                    versao: "^0.1".into(),
16265                },
16266            ],
16267        ];
16268        for membros in fixtures {
16269            let c = caixa_aplicacao_with_membros(membros.clone());
16270            assert_eq!(
16271                c.membros(),
16272                membros.as_slice(),
16273                "Caixa::membros must return :membros verbatim \
16274                 (got {:?}, expected {membros:?})",
16275                c.membros(),
16276            );
16277            assert_eq!(
16278                c.membros(),
16279                c.membros.as_slice(),
16280                "Caixa::membros must element-equal the raw \
16281                 `self.membros.as_slice()` field access across every \
16282                 value in the Vec<Membro> accept-set",
16283            );
16284            assert_eq!(
16285                c.membros().is_empty(),
16286                c.membros.is_empty(),
16287                "Caixa::membros().is_empty() must byte-equal \
16288                 self.membros.is_empty() — a presence-bit drift would \
16289                 silently split the paired Caixa::declared_mesh_slots \
16290                 mesh declared-slot enumerator's presence probe from \
16291                 the peer Caixa::aplicacao_view typed-view composer's \
16292                 fold-in path",
16293            );
16294        }
16295    }
16296
16297    #[test]
16298    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
16299        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
16300        // presence-probe arm must key off [`Caixa::membros`], not the
16301        // raw `!self.membros.is_empty()` field-probe. Structurally: a
16302        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
16303        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
16304        // declared-slot list (the presence bit is non-empty, so the
16305        // mesh kind-coherence gate must surface the slot as
16306        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
16307        // push the label (the "author omitted the slot entirely" arm
16308        // — the empty-slice partition the serde-default folds onto).
16309        // The pair jointly pins the accessor + declared-slot
16310        // enumerator composition: any future silent detour that had
16311        // the accessor collapse `[Membro { .. }]` to `[]` (a
16312        // `.filter(|m| m.nome() != "__reserved__")` projection) would
16313        // silently absorb the "declared but degenerate" arm at the
16314        // accessor boundary and the
16315        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16316        // coherence gate would silently accept a struct-literal
16317        // `Caixa` carrying the drift.
16318        //
16319        // Peer of the sibling
16320        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16321        // (2a1f907) and
16322        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16323        // (c17b51e) composition pins on the M2 `:upgrade-from` /
16324        // `:children` composite-slice arms — same "the enumerator gate
16325        // must route through the substrate-primitive typed dispatch"
16326        // discipline extended onto the M3 `:membros` composite-slice
16327        // arm, opening the M3 arm of the declared-slot enumerator's
16328        // routing invariant.
16329        use crate::aplicacao::Membro;
16330        let c = caixa_aplicacao_with_membros(vec![Membro {
16331            caixa: "cart".into(),
16332            versao: "^0.1".into(),
16333        }]);
16334        let slots = c.declared_mesh_slots();
16335        assert!(
16336            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16337            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
16338             `:membros` is non-empty — the accessor and the enumerator \
16339             gate must route through the same substrate-primitive \
16340             typed dispatch on the outer :membros presence bit (got \
16341             slots={slots:?})",
16342        );
16343        let c = caixa_aplicacao_with_membros(vec![]);
16344        let slots = c.declared_mesh_slots();
16345        assert!(
16346            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
16347            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
16348             when `:membros` is empty — the author-omitted arm must \
16349             route through the accessor's empty-slice return unchanged \
16350             (got slots={slots:?})",
16351        );
16352    }
16353
16354    #[test]
16355    fn aplicacao_view_membros_arm_routes_through_accessor() {
16356        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
16357        // fold-in arm must key off [`Caixa::membros`], not the raw
16358        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
16359        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
16360        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
16361        // member list through the accessor into the typed
16362        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
16363        // every entry the accessor surfaces must land in the view's
16364        // `membros` slot in the same order. The pair jointly pins the
16365        // accessor + view-composer composition: any future silent
16366        // detour that had the accessor return a fresh-cloned
16367        // `Vec<Membro>` copy would silently break the reference-
16368        // identity pin the peer `aplicacao_view` fold-in path reads
16369        // from — the fold would clone once more per accessor call
16370        // instead of borrowing the storage buffer verbatim once.
16371        //
16372        // Peer of the sibling
16373        // `aplicacao_view_politicas_arm_folds_through_accessor`
16374        // (5d23d29) /
16375        // `aplicacao_view_placement_arm_folds_through_accessor`
16376        // (4fb8074) /
16377        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
16378        // composition pins on the M3 `:politicas` / `:placement` /
16379        // `:entrada` outer-`Option<&Composite>` arms — extended here to
16380        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
16381        // closing the aplicacao-view composer's routing invariant on
16382        // the composite-slice input.
16383        use crate::aplicacao::Membro;
16384        let c = caixa_aplicacao_with_membros(vec![
16385            Membro {
16386                caixa: "cart".into(),
16387                versao: "^0.1".into(),
16388            },
16389            Membro {
16390                caixa: "pricing".into(),
16391                versao: "^0.2".into(),
16392            },
16393        ]);
16394        let view = c
16395            .aplicacao_view()
16396            .expect("Aplicacao kind must produce an aplicacao_view");
16397        assert_eq!(
16398            view.membros(),
16399            c.membros(),
16400            "aplicacao_view must fold Caixa::membros verbatim into \
16401             AplicacaoSpec::membros — the accessor and the view \
16402             composer must route through the same substrate-primitive \
16403             typed dispatch on the outer :membros slice (got view \
16404             membros={:?}, expected {:?})",
16405            view.membros(),
16406            c.membros(),
16407        );
16408    }
16409
16410    #[test]
16411    fn membros_projects_slice_by_borrow() {
16412        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
16413        // borrow — the returned slice borrows the underlying
16414        // `Vec<Membro>` storage of the `:membros` slot and the
16415        // accessor must not clone the backing `Vec` on every call.
16416        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16417        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16418        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16419        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16420        // `exe_projects_slice_by_borrow` 65d9527,
16421        // `servicos_projects_slice_by_borrow` 611f78b,
16422        // `deps_projects_slice_by_borrow` ad34b4e,
16423        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16424        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16425        // `children_projects_slice_by_borrow` c17b51e) on the sibling
16426        // outer top-level [`Caixa`] scalar-element and composite-
16427        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
16428        // slot composite-element `&[Composite]` axis: the accessor's
16429        // returned slice must borrow from `&self` (the returned
16430        // reference's lifetime is tied to `&self`), and calling the
16431        // accessor twice on the same [`Caixa`] must yield slices that
16432        // are pointer-equal (the underlying byte-buffer is the storage
16433        // `Vec`'s allocation, not a fresh copy) as well as value-equal
16434        // (idempotent, no side effects on `&self`).
16435        //
16436        // Pins against a future silent detour that returned an owned
16437        // `Vec<Membro>` (which would type-check but silently clone on
16438        // every call), a `&Vec<Membro>` return (which would leak the
16439        // backing `Vec`'s grow/push/reserve surface no downstream
16440        // consumer reaches for), or a one-arm-only accessor that
16441        // returned a saturating value on some sentinel input.
16442        use crate::aplicacao::Membro;
16443        for membros in [
16444            vec![],
16445            vec![Membro {
16446                caixa: "cart".into(),
16447                versao: "^0.1".into(),
16448            }],
16449            vec![
16450                Membro {
16451                    caixa: "cart".into(),
16452                    versao: "^0.1".into(),
16453                },
16454                Membro {
16455                    caixa: "pricing".into(),
16456                    versao: "^0.2".into(),
16457                },
16458            ],
16459        ] {
16460            let c = caixa_aplicacao_with_membros(membros.clone());
16461            let first = c.membros();
16462            let second = c.membros();
16463            assert_eq!(
16464                first, second,
16465                "Caixa::membros must be idempotent — two successive \
16466                 calls on the same &self must return the same &[Membro]",
16467            );
16468            assert_eq!(
16469                first.as_ptr(),
16470                second.as_ptr(),
16471                "Caixa::membros must borrow the underlying Vec<Membro> \
16472                 storage — two successive calls must return slices with \
16473                 the same backing pointer (a fresh Vec<Membro> clone \
16474                 would change the pointer on every call)",
16475            );
16476            assert_eq!(
16477                first,
16478                membros.as_slice(),
16479                "Caixa::membros must return :membros verbatim by borrow \
16480                 — got {first:?}, expected {membros:?}",
16481            );
16482        }
16483    }
16484
16485    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
16486
16487    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
16488        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16489        c.kind = CaixaKind::Aplicacao;
16490        c.contratos = contratos;
16491        c
16492    }
16493
16494    fn contrato_http_for_test(
16495        de: &str,
16496        para: &str,
16497        endpoint: &str,
16498    ) -> crate::aplicacao::WitContract {
16499        crate::aplicacao::WitContract {
16500            de: de.into(),
16501            para: para.into(),
16502            wit: "wasi:http/proxy".into(),
16503            endpoint: Some(endpoint.into()),
16504            subject: None,
16505            slot: None,
16506        }
16507    }
16508
16509    #[test]
16510    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
16511        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
16512        // composite `&[WitContract]`-return slice-shape pin:
16513        // [`Caixa::contratos`] must return the `:contratos` typed
16514        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
16515        // over the same backing buffer the raw
16516        // `self.contratos.as_slice()` field access borrows from,
16517        // element-equal across every representative fixture in the
16518        // accept-set — `[]` (the "no contracts declared" arm every
16519        // non-`Aplicacao`-kind `defcaixa` carries by
16520        // `#[serde(default)]` and every leaf-Aplicacao with a single
16521        // member carries), a canonical single-edge fixture (the
16522        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
16523        // edge), and a canonical multi-edge fixture with three distinct
16524        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
16525        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
16526        //
16527        // Pins against a future silent detour that returned an owned
16528        // `Vec<WitContract>` (which would type-check but silently clone
16529        // on every accessor call, breaking the zero-cost projection
16530        // every peer sibling slice accessor carries), an axis-shuffled
16531        // projection (a future detour that reordered edges through the
16532        // accessor would silently split the paired
16533        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
16534        // traversal input from the peer [`Self::aplicacao_view`] fold-
16535        // in path's clone-order input, since every canonical
16536        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
16537        // seed dispatch reads the edge set through the same slice),
16538        // or a reference to an operator-resolved overlay (the future
16539        // per-cluster `:contratos-overrides` slot — its resolution
16540        // must land at exactly this accessor body, not silently divert
16541        // the raw slot away from a second consumer).
16542        //
16543        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
16544        // accessor pin on the substrate primitive for M2 / M3 typed-
16545        // slot vec-carry axes — closes the outer-`Caixa`
16546        // `&[Composite]` composite-slice sub-family the sibling M2
16547        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
16548        // (2a1f907) and
16549        // `children_returns_children_slice_verbatim_across_permutations`
16550        // (c17b51e) pins opened and the M3
16551        // `membros_returns_membros_slice_verbatim_across_permutations`
16552        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
16553        // slot arm of the composite-slice sub-family. Peer at the outer
16554        // altitude of the closed inner-
16555        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
16556        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
16557        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
16558            vec![],
16559            vec![contrato_http_for_test("cart", "catalog", "/items")],
16560            vec![
16561                contrato_http_for_test("cart", "catalog", "/items"),
16562                contrato_http_for_test("cart", "pricing", "/price"),
16563                contrato_http_for_test("cart", "auth", "/whoami"),
16564            ],
16565        ];
16566        for contratos in fixtures {
16567            let c = caixa_aplicacao_with_contratos(contratos.clone());
16568            assert_eq!(
16569                c.contratos(),
16570                contratos.as_slice(),
16571                "Caixa::contratos must return :contratos verbatim \
16572                 (got {:?}, expected {contratos:?})",
16573                c.contratos(),
16574            );
16575            assert_eq!(
16576                c.contratos(),
16577                c.contratos.as_slice(),
16578                "Caixa::contratos must element-equal the raw \
16579                 `self.contratos.as_slice()` field access across every \
16580                 value in the Vec<WitContract> accept-set",
16581            );
16582            assert_eq!(
16583                c.contratos().is_empty(),
16584                c.contratos.is_empty(),
16585                "Caixa::contratos().is_empty() must byte-equal \
16586                 self.contratos.is_empty() — a presence-bit drift would \
16587                 silently split the paired Caixa::declared_mesh_slots \
16588                 mesh declared-slot enumerator's presence probe from \
16589                 the peer Caixa::aplicacao_view typed-view composer's \
16590                 fold-in path",
16591            );
16592        }
16593    }
16594
16595    #[test]
16596    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
16597        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
16598        // presence-probe arm must key off [`Caixa::contratos`], not the
16599        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
16600        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
16601        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
16602        // presence bit is non-empty, so the mesh kind-coherence gate
16603        // must surface the slot as "declared"), and a `Caixa {
16604        // contratos: vec![], .. }` must NOT push the label (the "author
16605        // omitted the slot entirely" arm — the empty-slice partition
16606        // the serde-default folds onto). The pair jointly pins the
16607        // accessor + declared-slot enumerator composition: any future
16608        // silent detour that had the accessor collapse
16609        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
16610        // "__reserved__")` projection) would silently absorb the
16611        // "declared but degenerate" arm at the accessor boundary and
16612        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16613        // coherence gate would silently accept a struct-literal
16614        // `Caixa` carrying the drift.
16615        //
16616        // Peer of the sibling
16617        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
16618        // (2a1f907),
16619        // `declared_supervisor_slots_children_arm_routes_through_accessor`
16620        // (c17b51e), and
16621        // `declared_mesh_slots_membros_arm_routes_through_accessor`
16622        // (0f26987) composition pins on the M2 `:upgrade-from` /
16623        // `:children` / M3 `:membros` composite-slice arms — same "the
16624        // enumerator gate must route through the substrate-primitive
16625        // typed dispatch" discipline extended onto the M3 `:contratos`
16626        // composite-slice arm, closing the M3 mesh-slot arm of the
16627        // declared-slot enumerator's routing invariant on the
16628        // composite-slice inputs.
16629        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
16630            "cart", "catalog", "/items",
16631        )]);
16632        let slots = c.declared_mesh_slots();
16633        assert!(
16634            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16635            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
16636             `:contratos` is non-empty — the accessor and the enumerator \
16637             gate must route through the same substrate-primitive \
16638             typed dispatch on the outer :contratos presence bit (got \
16639             slots={slots:?})",
16640        );
16641        let c = caixa_aplicacao_with_contratos(vec![]);
16642        let slots = c.declared_mesh_slots();
16643        assert!(
16644            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
16645            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
16646             when `:contratos` is empty — the author-omitted arm must \
16647             route through the accessor's empty-slice return unchanged \
16648             (got slots={slots:?})",
16649        );
16650    }
16651
16652    #[test]
16653    fn aplicacao_view_contratos_arm_routes_through_accessor() {
16654        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
16655        // fold-in arm must key off [`Caixa::contratos`], not the raw
16656        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
16657        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
16658        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
16659        // per-edge list through the accessor into the typed
16660        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
16661        // every entry the accessor surfaces must land in the view's
16662        // `contratos` slot in the same order. The pair jointly pins
16663        // the accessor + view-composer composition: a future silent
16664        // detour that had the accessor shuffle or drop an edge would
16665        // silently split the paired declared-slot enumerator's
16666        // presence bit from the typed-view composer's edge-list, a
16667        // two-consumer split at the enumerator and the view composer
16668        // far from the source `caixa.lisp`.
16669        //
16670        // Peer of the sibling
16671        // `aplicacao_view_membros_arm_routes_through_accessor`
16672        // (0f26987) composition pin on the M3 `:membros` outer-
16673        // `&[Composite]` composite-slice arm, closing the aplicacao-
16674        // view composer's routing invariant on the composite-slice
16675        // inputs at the outer altitude.
16676        let c = caixa_aplicacao_with_contratos(vec![
16677            contrato_http_for_test("cart", "catalog", "/items"),
16678            contrato_http_for_test("cart", "pricing", "/price"),
16679        ]);
16680        let view = c
16681            .aplicacao_view()
16682            .expect("Aplicacao kind must produce an aplicacao_view");
16683        assert_eq!(
16684            view.contratos(),
16685            c.contratos(),
16686            "aplicacao_view must fold Caixa::contratos verbatim into \
16687             AplicacaoSpec::contratos — the accessor and the view \
16688             composer must route through the same substrate-primitive \
16689             typed dispatch on the outer :contratos slice (got view \
16690             contratos={:?}, expected {:?})",
16691            view.contratos(),
16692            c.contratos(),
16693        );
16694    }
16695
16696    #[test]
16697    fn contratos_projects_slice_by_borrow() {
16698        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
16699        // by borrow — the returned slice borrows the underlying
16700        // `Vec<WitContract>` storage of the `:contratos` slot and the
16701        // accessor must not clone the backing `Vec` on every call.
16702        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
16703        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
16704        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
16705        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
16706        // `exe_projects_slice_by_borrow` 65d9527,
16707        // `servicos_projects_slice_by_borrow` 611f78b,
16708        // `deps_projects_slice_by_borrow` ad34b4e,
16709        // `deps_dev_projects_slice_by_borrow` f7fd81e,
16710        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
16711        // `children_projects_slice_by_borrow` c17b51e,
16712        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
16713        // outer top-level [`Caixa`] scalar-element and composite-
16714        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
16715        // composite-element `&[Composite]` axis on the by-borrow pin:
16716        // the accessor's returned slice must borrow from `&self` (the
16717        // returned reference's lifetime is tied to `&self`), and
16718        // calling the accessor twice on the same [`Caixa`] must yield
16719        // slices that are pointer-equal (the underlying byte-buffer is
16720        // the storage `Vec`'s allocation, not a fresh copy) as well as
16721        // value-equal (idempotent, no side effects on `&self`).
16722        //
16723        // Pins against a future silent detour that returned an owned
16724        // `Vec<WitContract>` (which would type-check but silently clone
16725        // on every call), a `&Vec<WitContract>` return (which would
16726        // leak the backing `Vec`'s grow/push/reserve surface no
16727        // downstream consumer reaches for), or a one-arm-only accessor
16728        // that returned a saturating value on some sentinel input.
16729        for contratos in [
16730            vec![],
16731            vec![contrato_http_for_test("cart", "catalog", "/items")],
16732            vec![
16733                contrato_http_for_test("cart", "catalog", "/items"),
16734                contrato_http_for_test("cart", "pricing", "/price"),
16735            ],
16736        ] {
16737            let c = caixa_aplicacao_with_contratos(contratos.clone());
16738            let first = c.contratos();
16739            let second = c.contratos();
16740            assert_eq!(
16741                first, second,
16742                "Caixa::contratos must be idempotent — two successive \
16743                 calls on the same &self must return the same \
16744                 &[WitContract]",
16745            );
16746            assert_eq!(
16747                first.as_ptr(),
16748                second.as_ptr(),
16749                "Caixa::contratos must borrow the underlying \
16750                 Vec<WitContract> storage — two successive calls must \
16751                 return slices with the same backing pointer (a fresh \
16752                 Vec<WitContract> clone would change the pointer on \
16753                 every call)",
16754            );
16755            assert_eq!(
16756                first,
16757                contratos.as_slice(),
16758                "Caixa::contratos must return :contratos verbatim by \
16759                 borrow — got {first:?}, expected {contratos:?}",
16760            );
16761        }
16762    }
16763
16764    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
16765
16766    #[test]
16767    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
16768        // Load-bearing invariant: every multi-word top-level [`Caixa`]
16769        // serde-derived JSON key routes through a lifted `&'static str`
16770        // const. The Rust field names are `snake_case`
16771        // (`deps_dev` / `upgrade_from` / `max_restarts` /
16772        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
16773        // "camelCase")]` derive attribute maps each to the camelCase
16774        // byte-string the [`Caixa::to_lisp`] round-trip's
16775        // `serde_json::to_value(self)` step lands under before
16776        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
16777        // to the kebab-case `:deps-dev` / `:upgrade-from` /
16778        // `:max-restarts` / `:restart-window` author surface. Serialize
16779        // a fully-populated [`Caixa`] and pin that each canonical
16780        // byte-sequence appears verbatim in the JSON — a future
16781        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
16782        // verbatim-field-name flip at the derive attribute (any of
16783        // which would silently break every [`Caixa::to_lisp`]
16784        // round-trip and the future M4 operator-side manifest ingest's
16785        // `Value::get(<key>)` navigation) surfaces here as a build-time
16786        // test failure at `manifest.rs`, not as an apply-time
16787        // `.get(<stale-canonical-const>)` returning `None` far from the
16788        // derive-attr drift's commit. Same discipline the sibling
16789        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
16790        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
16791        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
16792        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
16793        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
16794        // [`UpgradeFromEntry`] per-entry axes — extended here to the
16795        // enclosing M0 [`Caixa`] top-level axis so the last of the four
16796        // multi-word top-level [`Caixa`] serde-derived JSON keys
16797        // (`depsDev`) joins the substrate's "one canonical byte-string
16798        // per typed serialized-key axis" discipline.
16799        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16800        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16801        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16802        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
16803        c.upgrade_from = vec![UpgradeFromEntry {
16804            from: "0.0.1".into(),
16805            instructions: vec![UpgradeInstruction::Restart],
16806        }];
16807        c.estrategia = Some(RestartStrategy::OneForOne);
16808        c.max_restarts = Some(3);
16809        c.restart_window = Some("60s".into());
16810        c.children = vec![ChildSpec {
16811            caixa: "child".into(),
16812            versao: "^0.1".into(),
16813            restart: RestartPolicy::Permanent,
16814        }];
16815        let json = serde_json::to_string(&c).unwrap();
16816        for key in [
16817            crate::render::CAIXA_KEY_DEPS_DEV,
16818            crate::render::M2_KEY_UPGRADE_FROM,
16819            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16820            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16821        ] {
16822            let quoted = format!("\"{key}\"");
16823            assert!(
16824                json.contains(&quoted),
16825                "serialized Caixa must carry the lifted top-level \
16826                 multi-word byte-sequence {quoted} verbatim in the JSON \
16827                 emission (got: {json})",
16828            );
16829        }
16830    }
16831
16832    #[test]
16833    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
16834        // Cross-axis drift-detection pin: a future collapse of the four
16835        // canonical [`Caixa`] top-level multi-word byte-strings onto the
16836        // same value (e.g. an accidental copy-paste flip of
16837        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
16838        // `"upgradeFrom"`) would silently reroute every downstream
16839        // `Value::get(<key>)` probe on one axis onto the sibling axis's
16840        // top-level entry and pass every propagation-probe test that
16841        // expected only the stale axis's value. Peer of the sibling
16842        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
16843        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
16844        let all = [
16845            crate::render::CAIXA_KEY_DEPS_DEV,
16846            crate::render::M2_KEY_UPGRADE_FROM,
16847            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16848            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16849        ];
16850        for (i, a) in all.iter().enumerate() {
16851            for b in all.iter().skip(i + 1) {
16852                assert_ne!(
16853                    a, b,
16854                    "Caixa top-level multi-word key consts must be \
16855                     pairwise-distinct canonical byte-sequences — got \
16856                     `{a}` == `{b}`",
16857                );
16858            }
16859        }
16860    }
16861
16862    #[test]
16863    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
16864        // Shape-pin: every [`Caixa`] top-level multi-word key const must
16865        // be a lowerCamelCase byte-sequence (no `snake_case`
16866        // underscores, no `kebab-case` hyphens, no leading colon, no
16867        // `PascalCase` leading capital, no whitespace / dots) — the
16868        // canonical shape the `#[serde(rename_all = "camelCase")]`
16869        // derive produces on [`Caixa`]. A future flip to a
16870        // non-camelCase attribute at the derive surfaces both here
16871        // (this test fails on the stale-constant shape) and at
16872        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16873        // (that test fails on the mismatch between const and derive).
16874        // Peer with `membro_key_consts_are_lower_camel_case_shape`
16875        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
16876        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
16877        for key in [
16878            crate::render::CAIXA_KEY_DEPS_DEV,
16879            crate::render::M2_KEY_UPGRADE_FROM,
16880            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
16881            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
16882        ] {
16883            assert!(
16884                !key.is_empty(),
16885                "Caixa top-level multi-word key const must be non-empty \
16886                 (got {key:?})"
16887            );
16888            let first = key.chars().next().unwrap();
16889            assert!(
16890                first.is_ascii_lowercase(),
16891                "Caixa top-level multi-word key const must lead with an \
16892                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
16893            );
16894            assert!(
16895                key.chars().all(|c| c.is_ascii_alphanumeric()),
16896                "Caixa top-level multi-word key const must be \
16897                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
16898                 whitespace (got {key:?})",
16899            );
16900        }
16901    }
16902
16903    #[test]
16904    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
16905        // Scalar-value pin: the byte-string the
16906        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
16907        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
16908        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
16909        // → `depsTest` matching a hypothetical per-test-target
16910        // vocabulary flip) lands as an edit to exactly one const AND
16911        // one derive attribute — the sibling
16912        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16913        // pin already ties the const to the derive attribute, so a
16914        // rebrand that touches only one side of the pair fails at
16915        // caixa-core build time. Same "scalar-value pin per const"
16916        // discipline the sibling
16917        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
16918        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
16919        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
16920        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
16921    }
16922
16923    #[test]
16924    fn caixa_key_deps_pins_canonical_byte_string() {
16925        // Scalar-value pin: the byte-string the
16926        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
16927        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
16928        // on the two-list dep-graph serialized-key axis — the sibling
16929        // pin covers the multi-word `deps_dev → depsDev` camelCase
16930        // arm, this pin covers the single-word `deps → deps` no-op arm
16931        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
16932        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
16933        // axis and the emitted JSON key equals the source-side field
16934        // name byte-for-byte). A future [`crate::Caixa::deps`] field
16935        // rename (`deps` → `dependencies` matching Cargo's verbatim
16936        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
16937        // hypothetical per-runtime-target vocabulary flip) OR an added
16938        // `#[serde(rename = "…")]` explicit override lands as an edit
16939        // to exactly one const AND one derive-attr / field name — the
16940        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
16941        // pin ties the const to the emitted JSON key, so a rebrand
16942        // that touches only one side of the pair fails at caixa-core
16943        // build time.
16944        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
16945    }
16946
16947    #[test]
16948    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
16949        // Load-bearing invariant on the single-word `deps` top-level
16950        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
16951        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
16952        // `serde_json::to_value(self)` step emits. Serialize a
16953        // populated [`Caixa`] whose `:deps` slot carries at least one
16954        // entry (the `#[serde(default)]` attribute on the field emits
16955        // an empty `[]` even without members, but a non-empty vec
16956        // additionally covers the codec's per-`Dep`-entry emission
16957        // path) and pin that `"deps"` appears verbatim in the JSON
16958        // emission — a future accidental `rename_all = "snake_case"` /
16959        // `"kebab-case"` flip at the derive attribute (or an added
16960        // `#[serde(rename = "…")]` explicit override on the field, or
16961        // a Rust field rename) would break every [`Caixa::to_lisp`]
16962        // round-trip and the future M4 operator-side manifest ingest's
16963        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
16964        // build-time test failure at `manifest.rs`, not as an
16965        // apply-time `.get(<stale-canonical-const>)` returning `None`
16966        // far from the drift's commit. Peer of the sibling
16967        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
16968        // multi-word pin on the same M0 [`Caixa`] top-level
16969        // serialized-key axis, extended here to the single-word arm
16970        // the multi-word test's `rename_all = "camelCase"` sweep can't
16971        // reach (single-word `deps → deps` is a no-op the multi-word
16972        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
16973        // `\"restartWindow\"` byte-scan can never observe).
16974        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16975        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
16976        let json = serde_json::to_string(&c).unwrap();
16977        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
16978        assert!(
16979            json.contains(&quoted),
16980            "serialized Caixa must carry the lifted top-level `deps` \
16981             byte-sequence {quoted} verbatim in the JSON emission (got: \
16982             {json})",
16983        );
16984    }
16985
16986    #[test]
16987    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
16988        // Cross-axis drift-detection pin on the two-list dep-graph
16989        // renderer-side wire-key axis: a future collapse of the
16990        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
16991        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
16992        // same value (e.g. an accidental copy-paste flip of
16993        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
16994        // reroute every downstream `Value::get(<key>)` probe on one
16995        // axis onto the sibling axis's dep-list and pass every
16996        // propagation-probe test that expected only the stale axis's
16997        // value — a dev-only dep would land in the runtime closure at
16998        // publish time, or a runtime dep would be excluded from the
16999        // published lacre. Peer of the sibling four-way distinct pin
17000        // on the top-level multi-word tetrad
17001        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
17002        // and the two-way pin on the sibling
17003        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
17004        // author-facing arm (4da6fba's test), extended here to the
17005        // renderer-side wire-key arm of the same two-list dep-graph
17006        // axis so both halves of the "one canonical byte-string per
17007        // typed axis per (author, wire)" grid carry the same
17008        // distinct-ness discipline.
17009        assert_ne!(
17010            crate::render::CAIXA_KEY_DEPS,
17011            crate::render::CAIXA_KEY_DEPS_DEV,
17012            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
17013             canonical byte-sequences on the two-list dep-graph \
17014             renderer-side wire-key axis"
17015        );
17016    }
17017
17018    // ── DepList / Caixa::push_dep pin ────────────────────────────────
17019    //
17020    // The compounding pin: the two-arm closed-set typed enum
17021    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
17022    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
17023    // consumer of the top-level manifest's dep-mutation surface reads
17024    // through, and the typed dispatch [`Caixa::push_dep`] on the
17025    // substrate primitive folds the "select list → check within-list
17026    // dup → push" cascade onto one method call. Prior to this landing
17027    // the two axes lived across two `&'static str` constants
17028    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
17029    // set type carrying the pair; the `feira add` mutation site's
17030    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
17031    // caixa.deps }` dispatch expressed no compile-time link back to
17032    // the substrate primitive, and a future third dep-list axis would
17033    // have silently split at every open-coded mutation site.
17034
17035    #[test]
17036    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
17037        // Every arm returns the same `&'static str` the substrate's
17038        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
17039        // constants carry. A future rebrand on either constant reaches
17040        // the enum through one edit; a regression to inline literals
17041        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
17042        // quotes from the wire-format constants every consumer routes
17043        // through and this pin flags it at build time.
17044        assert_eq!(
17045            crate::dep::DepList::Prod.as_str(),
17046            crate::render::DEP_AUTHOR_KEY_DEPS
17047        );
17048        assert_eq!(
17049            crate::dep::DepList::Dev.as_str(),
17050            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17051        );
17052    }
17053
17054    #[test]
17055    fn dep_list_display_routes_through_as_str() {
17056        // Same as-str-through-Display convergence discipline the
17057        // sibling closed-set typed enums carry — a `format!("{list}")`
17058        // call must land byte-for-byte on the accessor's return so a
17059        // future consumer that formats the enum for a diagnostic line
17060        // reaches the same wire-format constant the wire-format
17061        // producers do.
17062        assert_eq!(
17063            format!("{}", crate::dep::DepList::Prod),
17064            crate::dep::DepList::Prod.as_str()
17065        );
17066        assert_eq!(
17067            format!("{}", crate::dep::DepList::Dev),
17068            crate::dep::DepList::Dev.as_str()
17069        );
17070    }
17071
17072    #[test]
17073    fn dep_list_all_enumerates_every_variant_once() {
17074        // Exhaustive-iteration pin — every arm appears exactly once in
17075        // `ALL`, matching the closed set the compiler enforces on the
17076        // sibling `match self` arms. A future variant addition that
17077        // extends only one method's match without extending `ALL`
17078        // would silently drop the new arm from every consumer that
17079        // iterates the slice.
17080        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
17081        assert!(variants.contains(&crate::dep::DepList::Prod));
17082        assert!(variants.contains(&crate::dep::DepList::Dev));
17083        assert_eq!(variants.len(), 2);
17084    }
17085
17086    #[test]
17087    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
17088        // Reverse projection on the two-list dep-graph axis: the
17089        // author-surface wire tag the sibling `as_str` emitter walks
17090        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
17091        // `Some(DepList::Prod)`. A regression that hand-rolled the
17092        // per-arm match without routing through the lifted
17093        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
17094        // future wire-tag rebrand and this pin flags it at build time.
17095        assert_eq!(
17096            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
17097            Some(crate::dep::DepList::Prod)
17098        );
17099    }
17100
17101    #[test]
17102    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
17103        // Peer of the `Prod`-arm pin on the dev-only axis: the
17104        // author-surface wire tag the sibling `as_str` emitter walks
17105        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
17106        // back to `Some(DepList::Dev)`. Same drift-detection posture
17107        // as the peer arm — the sibling method `match` arms are
17108        // compiler-checked exhaustive so a future variant addition
17109        // trips at build time.
17110        assert_eq!(
17111            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
17112            Some(crate::dep::DepList::Dev)
17113        );
17114    }
17115
17116    #[test]
17117    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
17118        // Every input outside the closed-set arm-string set the
17119        // sibling `as_str` emitter walks lands on the terminal `None`
17120        // fallback — no silent-accept surface. Sweeps a set of
17121        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
17122        // rebrand candidates, foreign wire tags, empty string) so a
17123        // future variant addition that widened one wire form without
17124        // extending the emitter's arm-set would trip the sibling
17125        // round-trip pin below rather than silently accepting the new
17126        // form here.
17127        for candidate in [
17128            "",
17129            "deps",
17130            "deps-dev",
17131            ":deps ",
17132            ":Deps",
17133            ":DEPS",
17134            ":build-dep",
17135            ":tool-dep",
17136            "prod",
17137            "dev",
17138        ] {
17139            assert_eq!(
17140                crate::dep::DepList::from_wire(candidate),
17141                None,
17142                "from_wire({candidate:?}) must return None; every input outside \
17143                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
17144                 the sibling as_str emitter walks lands on the terminal fallback",
17145            );
17146        }
17147    }
17148
17149    #[test]
17150    fn dep_list_round_trips_through_as_str_and_from_wire() {
17151        // Load-bearing round-trip pin: every arm the `ALL` iteration
17152        // exposes survives the `as_str` → `from_wire` composition
17153        // byte-for-byte. Same discipline the sibling closed-set enums
17154        // carry — `CaixaKind` /
17155        // `RestartStrategy` / `RestartPolicy` /
17156        // `PlacementStrategy` — extended onto the two-list dep-graph
17157        // axis. A future variant addition that extends `ALL` +
17158        // `as_str` without extending `from_wire` (or vice versa)
17159        // trips at build time on this iteration because the compiler
17160        // enforces exhaustiveness on the sibling `match self` arms.
17161        for &list in crate::dep::DepList::ALL {
17162            assert_eq!(
17163                crate::dep::DepList::from_wire(list.as_str()),
17164                Some(list),
17165                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
17166                 a silent split between the forward emitter and the reverse parser \
17167                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
17168            );
17169        }
17170    }
17171
17172    #[test]
17173    fn push_dep_routes_to_deps_slot_on_prod_arm() {
17174        // The `Prod` arm dispatches to the runtime-closure `:deps`
17175        // slot every downstream lacre-pipeline consumer resolves at
17176        // build time. A future arm that regressed to inline `&mut
17177        // self.deps_dev` on the `Prod` path would silently reroute
17178        // every runtime dep into the dev-only closure at publish time
17179        // — this pin refuses that regression.
17180        let src = Caixa::template("host");
17181        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17182        let before_deps = caixa.deps().len();
17183        let before_deps_dev = caixa.deps_dev().len();
17184        let dep = Dep {
17185            nome: "caixa-teia".to_string(),
17186            versao: "^0.1".to_string(),
17187            fonte: None,
17188            opcional: false,
17189            caracteristicas: Vec::new(),
17190        };
17191        caixa
17192            .push_dep(crate::dep::DepList::Prod, dep)
17193            .expect("first push into :deps succeeds");
17194        assert_eq!(caixa.deps().len(), before_deps + 1);
17195        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
17196        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
17197    }
17198
17199    #[test]
17200    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
17201        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
17202        // must dispatch to the dev-only-closure `:deps-dev` slot every
17203        // downstream test-facing artifact resolver reads. A future
17204        // regression that inverted the two arms would silently route
17205        // every dev-only dep into the runtime closure at publish time
17206        // and this pin catches it before the drift ships.
17207        let src = Caixa::template("host");
17208        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17209        let dep = Dep {
17210            nome: "tatara-check".to_string(),
17211            versao: "*".to_string(),
17212            fonte: None,
17213            opcional: false,
17214            caracteristicas: Vec::new(),
17215        };
17216        caixa
17217            .push_dep(crate::dep::DepList::Dev, dep)
17218            .expect("first push into :deps-dev succeeds");
17219        assert!(caixa.deps().is_empty());
17220        assert_eq!(caixa.deps_dev().len(), 1);
17221        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
17222    }
17223
17224    #[test]
17225    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
17226        // Within-list dup check routes through the canonical
17227        // [`DepError::DuplicateNome`] carrier — the substrate's typed
17228        // diagnostic for the same axis [`Caixa::validate_deps`]'s
17229        // parse-time [`crate::render::insert_first_seen`] walk raises
17230        // on. Prior to the lift the mutation site's inline
17231        // `bail!("dep '{}' already declared", …)` string-diagnostic
17232        // path expressed no through-line back to the typed error;
17233        // routing every dep-list refusal through one carrier means an
17234        // author reading a `feira add` refusal and a `feira build`
17235        // refusal reaches for the same corrective surface without
17236        // switching diagnostic idioms.
17237        let src = Caixa::template("host");
17238        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17239        let dep = Dep {
17240            nome: "caixa-teia".to_string(),
17241            versao: "^0.1".to_string(),
17242            fonte: None,
17243            opcional: false,
17244            caracteristicas: Vec::new(),
17245        };
17246        caixa
17247            .push_dep(crate::dep::DepList::Prod, dep.clone())
17248            .expect("first push succeeds");
17249        let dup = Dep {
17250            nome: "caixa-teia".to_string(),
17251            versao: "^0.2".to_string(),
17252            fonte: None,
17253            opcional: false,
17254            caracteristicas: Vec::new(),
17255        };
17256        let err = caixa
17257            .push_dep(crate::dep::DepList::Prod, dup)
17258            .expect_err("second push with same :nome refuses");
17259        assert_eq!(
17260            err,
17261            DepError::DuplicateNome {
17262                nome: "caixa-teia".to_string(),
17263                list: crate::render::DEP_AUTHOR_KEY_DEPS,
17264            }
17265        );
17266        // The refused mutation must not corrupt the target list —
17267        // exactly one entry lives past the refusal, matching the
17268        // canonical single-source-of-truth invariant `Caixa::deps()`
17269        // carries.
17270        assert_eq!(caixa.deps().len(), 1);
17271    }
17272
17273    #[test]
17274    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
17275        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
17276        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
17277        // `list` payload so a future author reading the refusal grep's
17278        // for the correct `:deps-dev` block in their `caixa.lisp`,
17279        // not the sibling `:deps` block the runtime closure resolves.
17280        let src = Caixa::template("host");
17281        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17282        let dep = Dep {
17283            nome: "tatara-check".to_string(),
17284            versao: "*".to_string(),
17285            fonte: None,
17286            opcional: false,
17287            caracteristicas: Vec::new(),
17288        };
17289        caixa
17290            .push_dep(crate::dep::DepList::Dev, dep.clone())
17291            .expect("first push succeeds");
17292        let err = caixa
17293            .push_dep(crate::dep::DepList::Dev, dep)
17294            .expect_err("second push with same :nome refuses");
17295        assert!(matches!(
17296            err,
17297            DepError::DuplicateNome {
17298                ref nome,
17299                list,
17300            } if nome == "tatara-check"
17301                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
17302        ));
17303    }
17304
17305    #[test]
17306    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
17307        // The within-list dup check is scoped to the target arm — a
17308        // caixa may legitimately carry the same `:nome` under both
17309        // `:deps` and `:deps-dev` (though the substrate's peer
17310        // [`crate::Caixa::validate_deps`] walk still refuses the
17311        // shape at parse time; the mutation-site refusal is scoped to
17312        // the mutation-site's list to match the peer parse-time
17313        // per-list [`crate::render::insert_first_seen`] discipline).
17314        // The two arms hold independent seen-sets.
17315        let src = Caixa::template("host");
17316        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17317        let dep_prod = Dep {
17318            nome: "shared".to_string(),
17319            versao: "^0.1".to_string(),
17320            fonte: None,
17321            opcional: false,
17322            caracteristicas: Vec::new(),
17323        };
17324        let dep_dev = Dep {
17325            nome: "shared".to_string(),
17326            versao: "*".to_string(),
17327            fonte: None,
17328            opcional: false,
17329            caracteristicas: Vec::new(),
17330        };
17331        caixa
17332            .push_dep(crate::dep::DepList::Prod, dep_prod)
17333            .expect("push into :deps succeeds");
17334        caixa
17335            .push_dep(crate::dep::DepList::Dev, dep_dev)
17336            .expect("push same :nome into :deps-dev succeeds");
17337        assert_eq!(caixa.deps().len(), 1);
17338        assert_eq!(caixa.deps_dev().len(), 1);
17339    }
17340
17341    #[test]
17342    fn deps_of_prod_returns_the_deps_slot_verbatim() {
17343        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
17344        // accessor must project onto the runtime-closure `:deps` slot —
17345        // element-equal and length-equal to the sibling per-slot
17346        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
17347        // A future arm that regressed to `self.deps_dev()` on the `Prod`
17348        // path would silently reroute every downstream typed-dispatch
17349        // walker (the [`Caixa::validate_deps`] per-list
17350        // [`crate::render::insert_first_seen`] dedup walk, any future
17351        // per-axis-parametrised consumer) into the sibling dev-only
17352        // closure and this pin refuses that regression.
17353        let src = Caixa::template("host");
17354        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17355        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17356        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
17357        let dep = Dep {
17358            nome: "caixa-teia".to_string(),
17359            versao: "^0.1".to_string(),
17360            fonte: None,
17361            opcional: false,
17362            caracteristicas: Vec::new(),
17363        };
17364        caixa
17365            .push_dep(crate::dep::DepList::Prod, dep.clone())
17366            .expect("push into :deps succeeds");
17367        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
17368        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
17369        assert_eq!(
17370            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
17371            "caixa-teia"
17372        );
17373    }
17374
17375    #[test]
17376    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
17377        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
17378        // [`Caixa::deps_of`] must project onto the dev-only-closure
17379        // `:deps-dev` slot, element-equal and length-equal to the
17380        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
17381        // future regression that inverted the two arms would silently
17382        // route every dev-list walker onto the runtime closure and this
17383        // pin catches it before the drift ships.
17384        let src = Caixa::template("host");
17385        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17386        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17387        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
17388        let dep = Dep {
17389            nome: "tatara-check".to_string(),
17390            versao: "*".to_string(),
17391            fonte: None,
17392            opcional: false,
17393            caracteristicas: Vec::new(),
17394        };
17395        caixa
17396            .push_dep(crate::dep::DepList::Dev, dep)
17397            .expect("push into :deps-dev succeeds");
17398        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
17399        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
17400        assert_eq!(
17401            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
17402            "tatara-check"
17403        );
17404    }
17405
17406    #[test]
17407    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
17408        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
17409        // [`Caixa::deps_of`] must land on the same two-slot partition the
17410        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
17411        // expose — the canonical dispatch a future per-axis-parametrised
17412        // walker (a future `feira app graph` per-list dep summary, a
17413        // future M4 per-cluster dev-closure-audit overlay the CR
17414        // materializer resolves per-CR) reads through. Prior to the
17415        // lift the two-block iteration lived open-coded at every walker,
17416        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
17417        // §I) would have had to grow a third block at every consumer.
17418        // A regression that dropped the `Dev` arm from `ALL` would flip
17419        // the collected pairs to `[(":deps", &[])]` alone and this pin
17420        // refuses that shape.
17421        let src = Caixa::template("host");
17422        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17423        let prod_dep = Dep {
17424            nome: "caixa-teia".to_string(),
17425            versao: "^0.1".to_string(),
17426            fonte: None,
17427            opcional: false,
17428            caracteristicas: Vec::new(),
17429        };
17430        let dev_dep = Dep {
17431            nome: "tatara-check".to_string(),
17432            versao: "*".to_string(),
17433            fonte: None,
17434            opcional: false,
17435            caracteristicas: Vec::new(),
17436        };
17437        caixa
17438            .push_dep(crate::dep::DepList::Prod, prod_dep)
17439            .expect("push into :deps succeeds");
17440        caixa
17441            .push_dep(crate::dep::DepList::Dev, dev_dep)
17442            .expect("push into :deps-dev succeeds");
17443        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
17444            .iter()
17445            .map(|&list| {
17446                let slice = caixa.deps_of(list);
17447                (list.as_str(), slice.len(), slice[0].nome())
17448            })
17449            .collect();
17450        assert_eq!(
17451            collected,
17452            vec![
17453                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
17454                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
17455            ]
17456        );
17457    }
17458
17459    #[test]
17460    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
17461        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
17462        // must route its per-list [`crate::render::insert_first_seen`]
17463        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
17464        // rather than the pre-lift open-coded two-block iteration over
17465        // `self.deps()` + `self.deps_dev()`. A regression that dropped
17466        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
17467        // stop refusing within-list dups on the sibling arm; a
17468        // regression that flipped the arm-to-list-key mapping
17469        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
17470        // diagnostic surface. Both drifts surface here through a paired
17471        // duplicate-name refusal per arm plus an offending-list-key
17472        // check on the emitted [`DepError::DuplicateNome`] carrier.
17473        for &list in crate::dep::DepList::ALL {
17474            let src = Caixa::template("host");
17475            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
17476            let dup = Dep {
17477                nome: "twin".to_string(),
17478                versao: "^0.1".to_string(),
17479                fonte: None,
17480                opcional: false,
17481                caracteristicas: Vec::new(),
17482            };
17483            match list {
17484                crate::dep::DepList::Prod => {
17485                    caixa.deps.push(dup.clone());
17486                    caixa.deps.push(dup);
17487                }
17488                crate::dep::DepList::Dev => {
17489                    caixa.deps_dev.push(dup.clone());
17490                    caixa.deps_dev.push(dup);
17491                }
17492            }
17493            let err = caixa
17494                .validate_deps()
17495                .expect_err("within-list duplicate :nome must refuse");
17496            assert_eq!(
17497                err,
17498                DepError::DuplicateNome {
17499                    nome: "twin".to_string(),
17500                    list: list.as_str(),
17501                },
17502                "validate_deps on {list} arm must emit \
17503                 DepError::DuplicateNome carrying the arm's own \
17504                 as_str() diagnostic — the arm-to-list-key mapping \
17505                 flowed through DepList::ALL + Caixa::deps_of"
17506            );
17507        }
17508    }
17509}