| 595 | }; |
| 596 | let changed = univ_idxs |
| 597 | .iter() |
| 598 | .zip(ingressed.iter()) |
| 599 | .any(|(&idx, k)| !ixon_matches_kuniv(stored_at(idx), k)); |
| 600 | if !changed { |
| 601 | return None; |
| 602 | } |
| 603 | let spellings: Box<[Arc<IxonUniv>]> = |
| 604 | univ_idxs.iter().map(|&idx| stored_at(idx).clone()).collect(); |
| 605 | Some(UnivDecor::Const(spellings)) |
| 606 | } |
| 607 | |
| 608 | /// Stage-2 decoration source for a `Const`-shaped occurrence |
| 609 | /// (canonicity §10.6): a `univ_patches` entry keyed by the occurrence's |
| 610 | /// (post-mdata) arena index carries the FULL original spelling list in |
| 611 | /// the virtual index space — decorate from it. Without a patch, fall |
| 612 | /// back to the stage-1 mk*-rebuild test against the stored primary |
| 613 | /// entries; on canonical tables the fallback never fires (P3), but it |
| 614 | /// keeps hand-built raw-table fixtures exercised. Meta mode only — call |
| 615 | /// inside `M::meta_field_try`. |
| 616 | fn univ_args_decor_at<M: KernelMode>( |
| 617 | arena_idx: u64, |
| 618 | univ_idxs: &[u64], |
| 619 | ctx: &Ctx<'_, M>, |
| 620 | ingressed: &[KUniv<M>], |
| 621 | ) -> Result<Option<UnivDecor>, String> { |
| 622 | let Some(patch_idxs) = ctx.univ_patch(arena_idx) else { |
| 623 | return Ok(univ_args_decor(univ_idxs, ctx, ingressed)); |
| 624 | }; |
| 625 | if patch_idxs.len() != ingressed.len() { |
| 626 | return Err(format!( |
| 627 | "univ patch at arena index {arena_idx} has {} entries but the \ |
| 628 | occurrence has {} level args", |
| 629 | patch_idxs.len(), |
| 630 | ingressed.len() |
| 631 | )); |
| 632 | } |
| 633 | let mut spellings = Vec::with_capacity(patch_idxs.len()); |
| 634 | for &vidx in patch_idxs { |
| 635 | spellings.push(ctx.univ_at_virtual(vidx)?.clone()); |
| 636 | } |
| 637 | let changed = spellings |
| 638 | .iter() |
| 639 | .zip(ingressed.iter()) |
| 640 | .any(|(s, k)| !ixon_matches_kuniv(s, k)); |
| 641 | Ok(if changed { |
| 642 | Some(UnivDecor::Const(spellings.into_boxed_slice())) |
| 643 | } else { |
| 644 | None |
| 645 | }) |
| 646 | } |
| 647 | |
| 648 | // ============================================================================ |
| 649 | // Expression ingress (iterative) |
| 650 | // ============================================================================ |
| 651 | |
| 652 | enum ExprFrame<M: KernelMode> { |
| 653 | Process { |
| 654 | expr: Arc<IxonExpr>, |