You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implements wiki ADR-055 (Universal substrate-Term verb body discipline,
supersedes ADR-054 RA2, amends ADR-010).
The `SubstrateTermBody` supertrait makes fold-fusion structurally
universal for every axis impl, not just the standard library's
canonical impls. Application-author custom axes now structurally carry
their substrate-Term decomposition — or the empty-arena primitive-fast-
path interpretation that preserves backward-compatibility while still
satisfying the type-system bound.
Foundation surface additions:
- `pub trait SubstrateTermBody: __sdk_seal::Sealed { fn body_arena() -> &'static [Term]; }`
on `uor_foundation::pipeline`. Sealed via `__sdk_seal::Sealed`; emission
is via the `axis!` SDK macro per ADR-030 + ADR-052.
- `pub trait AxisExtension: SubstrateTermBody { ... }` — the supertrait
bound is now structural, not opt-in (ADR-054 RA2 reversal).
- `AxisTuple::body_arena_at(axis_index) -> &'static [Term]` — per-axis
body dispatcher the catamorphism queries to decide between the
primitive fast-path and the recursive-fold path.
- All AxisTuple impls (Hasher blanket, 1-tuple through 8-tuple) emit
`body_arena_at` that delegates to each axis position's `SubstrateTermBody`.
Catamorphism fold-rule amendment (ADR-029):
- `Term::AxisInvocation` reads `<A as AxisTuple>::body_arena_at(axis_index)`.
Empty slice → primitive fast-path via `dispatch_kernel` (existing
behavior). Non-empty slice → recursively folds the body's Term arena
with the evaluated kernel input bound as `input_bytes`, walking
through every axis-surface kernel structurally.
SDK macro emission (axis_extension_impl_for_*!):
- Both non-generic and `@generic` arms now emit
`impl __sdk_seal::Sealed for $struct` and
`impl SubstrateTermBody for $struct { fn body_arena() -> &[] }`
alongside the existing `impl AxisExtension`. Existing axis impls
migrate without source changes (the empty-arena default is the
primitive-fast-path interpretation).
HashAxis<H> (foundation-built adapter):
- Emits `impl SubstrateTermBody for HashAxis<H> { fn body_arena() -> &[] }`
+ `impl __sdk_seal::Sealed for HashAxis<H>`. The canonical hash fold is
the primitive fast-path; `fold_bytes` ∘ `finalize` is byte-output-
equivalent to its (empty) substrate-Term decomposition.
Substrate amendment:
- `TRACE_REPLAY_FORMAT_VERSION` bumped 8 → 9 to reflect the new trait
surface. The on-wire shape of `Term::AxisInvocation` is unchanged
(body_arena() is a static const, not wire-format state); the version
bump records the trait-bound discipline change per ADR-013/TR-08.
- `rust/trace_byte_layout_pinned` conformance validator updated to
pin format-version 9.
Conformance test coverage:
- foundation/tests/behavior_adr_055_substrate_term_body.rs (6 tests)
pins: SubstrateTermBody public-path reachability, AxisExtension's
SubstrateTermBody supertrait bound, HashAxis primitive-fast-path body,
AxisTuple body_arena_at dispatch, Hasher-blanket body_arena_at, and
the catamorphism's primitive-fast-path branch via evaluate_term_tree.
- foundation/tests/public-api.snapshot + .nostd: add
`trait SubstrateTermBody`.
- conformance/endpoint_coverage.toml: ADR-055 coverage mapping.
Future work (not blocking this release):
- The `axis!` macro's `body` clause grammar extension — application
authors who want recursive fold-fusion through their axis kernel will
declare a substrate-Term composition via the `verb!`-style closure-
body grammar. The trait surface is in place; the macro emission
defaults to empty body_arena (primitive fast-path) until applications
opt in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
f.line(" // (axis_index, kernel_id) combinations and may provide substrate-");
4983
+
f.line(" // Term bodies the catamorphism walks structurally.");
4916
4984
f.line(" let v = evaluate_term_at::<A, R>(arena, input_index as usize, input_bytes, recurse_value, recurse_idx_value, unfold_value, first_admit_idx_value, resolvers)?;");
4917
-
f.line(" let mut out = [0u8; AXIS_OUTPUT_BYTES_CEILING];");
4918
-
f.line(" let written = match <A as crate::pipeline::AxisTuple>::dispatch(axis_index, kernel_id, v.bytes(), &mut out) {");
0 commit comments