@@ -7559,19 +7559,70 @@ fn emit_phase_j_primitives(f: &mut RustFile) {
75597559 f.doc_comment("differently-shaped complex than the caller asked about. Callers propagate");
75607560 f.doc_comment("the witness via `?` (pattern mirrored on `primitive_terminal_reduction`).");
75617561 f.doc_comment("");
7562+ f.doc_comment("ADR-057: `T::CONSTRAINTS` may contain `ConstraintRef::Recurse` entries");
7563+ f.doc_comment("referencing shapes by IRI. This primitive expands Recurse references");
7564+ f.doc_comment("through the **foundation built-in shape-IRI registry** (`lookup_shape`),");
7565+ f.doc_comment("decrementing the descent budget on each Recurse encountered and");
7566+ f.doc_comment("terminating when the budget reaches zero. Applications that register");
7567+ f.doc_comment("their own shapes (via the SDK `register_shape!` macro) use");
7568+ f.doc_comment("[`primitive_simplicial_nerve_betti_in`] which is generic over the");
7569+ f.doc_comment("application's `ShapeRegistryProvider`. The structural reading at ψ_1");
7570+ f.doc_comment("reflects the expanded constraint geometry — Recurse entries are not");
7571+ f.doc_comment("opaque anonymous Sites but their structurally-substituted body per the");
7572+ f.doc_comment("registered shape's `CONSTRAINTS` array.");
7573+ f.doc_comment("");
75627574 f.doc_comment("# Errors");
75637575 f.doc_comment("");
7564- f.doc_comment("Returns `NERVE_CAPACITY_EXCEEDED` when either cap is exceeded.");
7576+ f.doc_comment("Returns `NERVE_CAPACITY_EXCEEDED` when either cap is exceeded after");
7577+ f.doc_comment("expansion. Returns `RECURSE_SHAPE_UNREGISTERED` when a `Recurse`");
7578+ f.doc_comment("entry references an IRI not present in the consulted registry");
7579+ f.doc_comment("(non-zero descent budget).");
75657580 f.line("pub fn primitive_simplicial_nerve_betti<T: crate::pipeline::ConstrainedTypeShape + ?Sized>() -> Result<[u32; MAX_BETTI_DIMENSION], GenericImpossibilityWitness> {");
7566- f.line(" let k_all = T::CONSTRAINTS.len();");
7567- f.line(" if k_all > NERVE_CONSTRAINTS_CAP {");
7581+ f.line(" // ADR-057: foundation-default registry path. EmptyShapeRegistry's");
7582+ f.line(" // REGISTRY is the empty slice, so lookup_shape_in falls through to");
7583+ f.line(" // foundation's built-in FOUNDATION_REGISTRY (the canonical stdlib path).");
7584+ f.line(" primitive_simplicial_nerve_betti_in::<T, crate::pipeline::shape_iri_registry::EmptyShapeRegistry>()");
7585+ f.line("}");
7586+ f.blank();
7587+ f.doc_comment("ADR-057: registry-parameterized variant of");
7588+ f.doc_comment("[`primitive_simplicial_nerve_betti`]. Walks `T::CONSTRAINTS` and expands");
7589+ f.doc_comment("every `ConstraintRef::Recurse { shape_iri, descent_bound }` entry by");
7590+ f.doc_comment("looking up `shape_iri` through `R`'s registry plus foundation's");
7591+ f.doc_comment("built-in registry, decrementing the descent budget on each recursive");
7592+ f.doc_comment("walk, and terminating when the budget reaches zero. The expanded");
7593+ f.doc_comment("constraint sequence is the input to the nerve computation — the");
7594+ f.doc_comment("structural reading at ψ_1 reflects the recursive grammar.");
7595+ f.doc_comment("");
7596+ f.doc_comment("This is the entry point ψ_1 `NerveResolver` impls call from the");
7597+ f.doc_comment("application's resolver-tuple — `R` is the ResolverTuple's");
7598+ f.doc_comment("`ShapeRegistry` associated type per ADR-036+ADR-057.");
7599+ f.doc_comment("");
7600+ f.doc_comment("# Errors");
7601+ f.doc_comment("");
7602+ f.doc_comment("Returns `NERVE_CAPACITY_EXCEEDED` when the expanded constraint set");
7603+ f.doc_comment("exceeds `NERVE_CONSTRAINTS_CAP` or `NERVE_SITES_CAP`. Returns");
7604+ f.doc_comment("`RECURSE_SHAPE_UNREGISTERED` when a `Recurse` entry references an");
7605+ f.doc_comment("IRI not present in either `R::REGISTRY` or foundation's built-in.");
7606+ f.line("pub fn primitive_simplicial_nerve_betti_in<");
7607+ f.line(" T: crate::pipeline::ConstrainedTypeShape + ?Sized,");
7608+ f.line(" R: crate::pipeline::shape_iri_registry::ShapeRegistryProvider,");
7609+ f.line(">() -> Result<[u32; MAX_BETTI_DIMENSION], GenericImpossibilityWitness> {");
7610+ f.line(" // ADR-057 step 3: expand T::CONSTRAINTS, walking ConstraintRef::Recurse");
7611+ f.line(" // through R's registry with bounded descent.");
7612+ f.line(" let mut expanded: [crate::pipeline::ConstraintRef; NERVE_CONSTRAINTS_CAP] =");
7613+ f.line(
7614+ " [crate::pipeline::ConstraintRef::Site { position: 0 }; NERVE_CONSTRAINTS_CAP];",
7615+ );
7616+ f.line(" let mut n_expanded: usize = 0;");
7617+ f.line(" expand_constraints_in::<R>(T::CONSTRAINTS, u32::MAX, &mut expanded, &mut n_expanded)?;");
7618+ f.line(" let n_constraints = n_expanded;");
7619+ f.line(" if n_constraints > NERVE_CONSTRAINTS_CAP {");
75687620 f.line(" return Err(GenericImpossibilityWitness::for_identity(\"NERVE_CAPACITY_EXCEEDED\"));");
75697621 f.line(" }");
75707622 f.line(" let s_all = T::SITE_COUNT;");
75717623 f.line(" if s_all > NERVE_SITES_CAP {");
75727624 f.line(" return Err(GenericImpossibilityWitness::for_identity(\"NERVE_CAPACITY_EXCEEDED\"));");
75737625 f.line(" }");
7574- f.line(" let n_constraints = k_all;");
75757626 f.line(" let n_sites = s_all;");
75767627 f.line(" let mut out = [0u32; MAX_BETTI_DIMENSION];");
75777628 f.line(" if n_constraints == 0 {");
@@ -7582,7 +7633,7 @@ fn emit_phase_j_primitives(f: &mut RustFile) {
75827633 f.line(" let mut support = [0u16; NERVE_CONSTRAINTS_CAP];");
75837634 f.line(" let mut c = 0;");
75847635 f.line(" while c < n_constraints {");
7585- f.line(" support[c] = constraint_site_support_mask::<T>(c , n_sites);");
7636+ f.line(" support[c] = constraint_site_support_mask_of(&expanded[c] , n_sites);");
75867637 f.line(" c += 1;");
75877638 f.line(" }");
75887639 f.line(" // Enumerate 1-simplices: pairs (i,j) with i<j and support[i] & support[j] != 0.");
@@ -7668,6 +7719,78 @@ fn emit_phase_j_primitives(f: &mut RustFile) {
76687719 f.line(" Ok(out)");
76697720 f.line("}");
76707721 f.blank();
7722+
7723+ // ── ADR-057: Recurse expansion helper ─────────────────────────────────
7724+ f.doc_comment("ADR-057 step 3: walk `in_constraints`, copying non-Recurse entries into");
7725+ f.doc_comment("`out_arr` and expanding every `ConstraintRef::Recurse { shape_iri,");
7726+ f.doc_comment("descent_bound }` by looking up `shape_iri` through `R`'s registry plus");
7727+ f.doc_comment("foundation's built-in registry and recursing into the referenced shape's");
7728+ f.doc_comment("`CONSTRAINTS`. The effective descent budget at each Recurse is the min");
7729+ f.doc_comment("of the caller's `descent_remaining` and the constraint's own");
7730+ f.doc_comment("`descent_bound`; on Recurse the budget decrements by 1 before recursion.");
7731+ f.doc_comment("A budget of 0 terminates the descent (the Recurse contributes no");
7732+ f.doc_comment("further constraints — the recursion bottoms out).");
7733+ f.doc_comment("");
7734+ f.doc_comment("# Errors");
7735+ f.doc_comment("");
7736+ f.doc_comment("Returns `NERVE_CAPACITY_EXCEEDED` when the expansion would exceed");
7737+ f.doc_comment("`NERVE_CONSTRAINTS_CAP`. Returns `RECURSE_SHAPE_UNREGISTERED` when a");
7738+ f.doc_comment("`Recurse` entry with non-zero effective budget references an IRI not");
7739+ f.doc_comment("present in either `R::REGISTRY` or foundation's built-in registry.");
7740+ f.line(
7741+ "pub fn expand_constraints_in<R: crate::pipeline::shape_iri_registry::ShapeRegistryProvider>(",
7742+ );
7743+ f.line(" in_constraints: &[crate::pipeline::ConstraintRef],");
7744+ f.line(" descent_remaining: u32,");
7745+ f.line(" out_arr: &mut [crate::pipeline::ConstraintRef; NERVE_CONSTRAINTS_CAP],");
7746+ f.line(" out_n: &mut usize,");
7747+ f.line(") -> Result<(), GenericImpossibilityWitness> {");
7748+ f.line(" let mut i = 0;");
7749+ f.line(" while i < in_constraints.len() {");
7750+ f.line(" match in_constraints[i] {");
7751+ f.line(" crate::pipeline::ConstraintRef::Recurse { shape_iri, descent_bound } => {");
7752+ f.line(" // Effective budget = min(caller's remaining, this Recurse's bound).");
7753+ f.line(" let budget = if descent_remaining < descent_bound {");
7754+ f.line(" descent_remaining");
7755+ f.line(" } else {");
7756+ f.line(" descent_bound");
7757+ f.line(" };");
7758+ f.line(" if budget == 0 {");
7759+ f.line(" // Bottom out — contribute no further constraints.");
7760+ f.line(" } else {");
7761+ f.line(" match crate::pipeline::shape_iri_registry::lookup_shape_in::<R>(shape_iri) {");
7762+ f.line(" Some(registered) => {");
7763+ f.line(" expand_constraints_in::<R>(");
7764+ f.line(" registered.constraints,");
7765+ f.line(" budget - 1,");
7766+ f.line(" out_arr,");
7767+ f.line(" out_n,");
7768+ f.line(" )?;");
7769+ f.line(" }");
7770+ f.line(" None => {");
7771+ f.line(" return Err(GenericImpossibilityWitness::for_identity(");
7772+ f.line(" \"RECURSE_SHAPE_UNREGISTERED\",");
7773+ f.line(" ));");
7774+ f.line(" }");
7775+ f.line(" }");
7776+ f.line(" }");
7777+ f.line(" }");
7778+ f.line(" other => {");
7779+ f.line(" if *out_n >= NERVE_CONSTRAINTS_CAP {");
7780+ f.line(" return Err(GenericImpossibilityWitness::for_identity(");
7781+ f.line(" \"NERVE_CAPACITY_EXCEEDED\",");
7782+ f.line(" ));");
7783+ f.line(" }");
7784+ f.line(" out_arr[*out_n] = other;");
7785+ f.line(" *out_n += 1;");
7786+ f.line(" }");
7787+ f.line(" }");
7788+ f.line(" i += 1;");
7789+ f.line(" }");
7790+ f.line(" Ok(())");
7791+ f.line("}");
7792+ f.blank();
7793+
76717794 f.doc_comment("Phase X.4: cap on the number of constraints considered by the nerve");
76727795 f.doc_comment("primitive. Phase 1a (orphan-closure): inputs exceeding this cap are");
76737796 f.doc_comment("rejected via `NERVE_CAPACITY_EXCEEDED` (was previously silent truncation).");
@@ -7702,14 +7825,21 @@ fn emit_phase_j_primitives(f: &mut RustFile) {
77027825 f.line("pub(crate) const NERVE_RANK_MOD_P: i64 = 1_000_000_007;");
77037826 f.blank();
77047827 f.doc_comment("Phase X.4: per-constraint site-support bitmask. Returns bit `s` set iff");
7705- f.doc_comment("constraint `c` in `T::CONSTRAINTS` touches site index `s` (`s < n_sites`).");
7828+ f.doc_comment("constraint `c` touches site index `s` (`s < n_sites`).");
77067829 f.doc_comment("`Affine { coefficients, .. }` returns the bitmask of sites whose");
77077830 f.doc_comment("coefficient is non-zero — the natural \"site support\" of the affine");
77087831 f.doc_comment("relation. Remaining non-site-local variants (Residue, Hamming, Depth,");
77097832 f.doc_comment("Bound, Conjunction, SatClauses) return an all-ones mask over `n_sites`.");
7710- f.line("pub(crate) const fn constraint_site_support_mask<T: crate::pipeline::ConstrainedTypeShape + ?Sized>(c: usize, n_sites: usize) -> u16 {");
7833+ f.doc_comment("");
7834+ f.doc_comment("ADR-057: slice-based — operates directly on a `&ConstraintRef` rather");
7835+ f.doc_comment("than indexing a `ConstrainedTypeShape::CONSTRAINTS` slot. ψ_1 calls this");
7836+ f.doc_comment("from [`primitive_simplicial_nerve_betti_in`] after `T::CONSTRAINTS` has");
7837+ f.doc_comment("been expanded into a fixed-size array via [`expand_constraints_in`].");
7838+ f.line(
7839+ "pub(crate) const fn constraint_site_support_mask_of(c: &crate::pipeline::ConstraintRef, n_sites: usize) -> u16 {",
7840+ );
77117841 f.line(" let all_mask: u16 = if n_sites == 0 { 0 } else { (1u16 << n_sites) - 1 };");
7712- f.line(" match &T::CONSTRAINTS[c] {");
7842+ f.line(" match c {");
77137843 f.line(" crate::pipeline::ConstraintRef::Site { position } => {");
77147844 f.line(" if n_sites == 0 { 0 } else { 1u16 << (*position as usize % n_sites) }");
77157845 f.line(" }");
@@ -7730,6 +7860,9 @@ fn emit_phase_j_primitives(f: &mut RustFile) {
77307860 f.line(" if mask == 0 { all_mask } else { mask }");
77317861 f.line(" }");
77327862 f.line(" }");
7863+ f.line(" // ADR-057: any Recurse entry left in the array means");
7864+ f.line(" // expand_constraints_in already bottomed out (descent_bound = 0).");
7865+ f.line(" // Treat it as a structural placeholder with no specific site support.");
77337866 f.line(" _ => all_mask,");
77347867 f.line(" }");
77357868 f.line("}");
0 commit comments