Skip to content

11 Technical Risks

Alex Flom edited this page May 22, 2026 · 20 revisions

Risks and Technical Debts

This chapter enumerates known risks and technical debts in Prism’s architecture and in conforming implementations. An entry listed here is a structural property an implementation must navigate; it is not implementation discretion. Every entry has a name, a description, an architectural impact, and a mitigation posture.

The risks are not exhaustive. Operational risks (build pipeline failures, CI infrastructure outages, dependency supply-chain compromise) are out of scope; this chapter covers risks intrinsic to Prism’s architecture as specified in this wiki.

TR-01 — generic_const_exprs incompleteness on Rust nightly

Description: uor-foundation's internal proc-macro sub-crate enables #![feature(generic_const_exprs)] because the shape proc-macros (product_shape!, coproduct_shape!, cartesian_product_shape!) emit ConstrainedTypeShape impls that reference <H as HostBounds>::CAP at array-size positions. The feature is incomplete in Rust nightly and has been so for an extended period; specific complex const-expression patterns can fail to type-check or fail to monomorphize even when the pattern is logically well-formed. The macros are reached by application-author code through prism's vocabulary re-exports of uor-foundation's internal proc-macro sub-crate.

Architectural impact: An application author who writes a ConstrainedTypeShape declaration that exercises a const-expression pattern the compiler cannot yet resolve will see a compilation error that does not point at a UORassembly violation but at a toolchain limitation. This blurs the diagnostic distinction QS-04 specifies: the author cannot tell from the error whether the contract is violated or the toolchain is incomplete.

Mitigation posture: The shape proc-macros emit const-expression patterns the toolchain handles correctly today. The set of expressible shapes is bounded by what the toolchain supports; new shape variants prism might want to expose are gated both by uor-foundation's proc-macro emissions (where the feature opt-in lives) and by toolchain progress. The wiki’s UORassembly contract (section 8) is specified independently of the toolchain’s current feature implementation; if a future Rust release stabilizes generic_const_exprs or removes its incompleteness, the contract does not change. The risk is therefore one of time-bounded toolchain quality, not of architectural drift. Per ADR-060, the value-carrier surface specifically avoids this feature: the inline carrier width and the per-ψ-stage carrier widths derive from plain const fn calls on the application’s already-committed HostBounds constants (carrier_inline_bytes::<B>(), nerve_carrier_bytes::<B>(), …), which are admissible in array-length position on stable Rust, rather than generic_const_exprs arithmetic over generic parameters. The contrived 4096-byte carrier ceiling that pre-0.5.0 foundation pinned because generic_const_exprs was unstable is removed; the feature’s blast radius narrows to the shape-macro path this entry describes.

TR-02 — Gauge-level fixity at compile time

Description: Prism’s substitution axes are fixed at the application author’s compile time. An author cannot change the HostTypes, HostBounds, or AxisTuple selection (per ADR-030; the third position originally Hasher per ADR-007, generalized to a tuple of axis trait selections) at runtime; nor can they support multiple selections in a single executable without pre-compiling each variant separately. This is a direct consequence of constraint TC-01 (zero-cost runtime) and ADR-007 (substitution-axis allocation).

Architectural impact: An application that needs to operate against multiple substitution-axis configurations — for example, a service that accepts inputs hashed under SHA-256 from some clients and BLAKE3 from others — cannot be a single Prism executable. It must be either multiple executables or a non-Prism executable that delegates to Prism executables per-input.

Mitigation posture: This is a deliberate architectural property, not a defect. The mitigation, if an application’s domain requires multi-substrate operation, is at the application architecture level: compose multiple Prism executables, or use a meta-tool that selects among them. The wiki does not specify the meta-tool.

TR-03 — Host-platform feature-flag combinatorics

Description: All three crates — uor-foundation, prism, and prism-verify — expose feature flags alloc, std, serde, observability (section 8). prism's flags are consistent with uor-foundation's (additive across the dependency graph). The author selects feature flags as part of their crate’s Cargo.toml. Feature combinations now interact across two crate boundaries: a crate that depends on prism with alloc enabled and on a third-party crate that depends on uor-foundation (or another prism consumer) with std enabled triggers Cargo’s feature-unification, producing a build with both features active in both prism and uor-foundation. The unified build’s behavior is normatively specified — feature flags are additive (section 8) — but the compile-time and binary-size consequences vary across combinations and span both crates.

Architectural impact: An author whose crate composes with other crates that depend on prism or uor-foundation may not have full control over which features are active at build time. The author’s intended #![no_std] posture, for example, may be silently overridden if any transitive dependency enables std on either crate. The combinatoric space is now the cross-product of features at the uor-foundation and prism boundaries; with prism-verify participating in some dependency graphs, the cross-product extends to three boundaries.

Mitigation posture: Authors who require a specific feature posture can pin their dependency on prism (and indirectly uor-foundation) to a build profile that disables default features and explicitly enables only the flags they require (default-features = false plus an explicit features = […​] list, in Cargo.toml, for each Prism crate the author depends on). Cargo’s feature-unification still operates, so a transitive enabler of std will still cause std to be enabled, but this is detectable through cargo tree and similar inspection. The wiki specifies the additive property of features; the application author’s responsibility is to manage the feature graph across all three Prism crates for their crate’s intended deployment.

TR-04 — Distribution channel selection is out of architectural scope

Description: Prism is silent on the distribution channel by which the executable reaches the user (ADR-004). This is an architectural property — Prism’s specification does not constrain the channel — but it is also a risk: the absence of a normative channel means the channel’s properties (integrity, authenticity, availability) are application-author concerns that the architecture does not address.

Architectural impact: A compromised distribution channel can deliver a different executable than the author published. The user’s verification of an output (Scenario 2) confirms the output’s claims given the trace; it does not confirm the executable that produced the trace was the one the author intended. These are orthogonal properties; Scenario 4 in section 6 calls this out explicitly.

Mitigation posture: Application authors who require executable-authenticity guarantees adopt a supply-chain integrity mechanism out-of-band: code signing, content-addressable distribution (e.g., signed Git tags, content-addressed package registries, blockchain-anchored references), reproducible builds, attested build pipelines. Prism does not specify any of these; the application author selects whichever mechanism their threat model requires. The wiki acknowledges the orthogonality; conforming implementations are not responsible for solving it.

TR-05 — Hasher selection mismatch produces verification failure indistinguishable from data corruption

Description: The verifier rejects a trace with ReplayError::HasherMismatch if the supplied hasher_instance's identifier does not match the trace’s hasher_identifier. The user, faced with this error, cannot distinguish from the error alone whether: (a) they have the wrong Hasher instantiated; (b) the trace was produced with a Hasher whose identifier conflicts with another Hasher in the wider ecosystem; (c) the trace was tampered with to alter the identifier.

Architectural impact: Diagnosing why a verification fails is harder for the user than diagnosing why an execution fails. The execution’s error model (section 8) returns typed impossibility witnesses with positional information; the verification’s error model returns identifier-level errors that do not localize within the trace.

Mitigation posture: The application author’s documentation should communicate the Hasher identifier alongside any distributed traces, in a form that is not itself the trace (e.g., human-readable text the user can confirm matches what their verification environment expects). Prism’s wiki does not specify this communication format; it is the application author’s responsibility. The risk is that an author who skips this step leaves users unable to diagnose verification failures.

TR-06 — Trace format evolution requires version coordination

Description: The trace wire format carries a format_version field equal to TRACE_REPLAY_FORMAT_VERSION. The verifier rejects traces whose format version it does not recognize (section 8). If the wiki’s normative specification of the trace format is revised in a future revision of this wiki, the resulting format will have a different version number; existing traces will not be verifiable by the new verifier and new traces will not be verifiable by old verifiers. The format is defined in uor-foundation (data definitions in bridge); produced by prism's pipeline; consumed by prism-verify (façade) re-exporting prism's replay implementation. Format-version evolution therefore spans two repositories (UOR-Foundation/UOR-Framework for uor-foundation; UOR-Foundation/Prism for prism and prism-verify).

Architectural impact: Verification is bound to the trace format version, not just the trace content. Long-lived traces — produced today, verified years from now — require the verifier in use at verification time to recognize the format version the trace was produced with. Cross-repo coordination is now required for any format-version bump: uor-foundation is amended first (the data definitions change), then prism is repinned against the new uor-foundation and amended for the new producer/consumer paths, then prism-verify follows automatically as a thin façade.

Mitigation posture: The wire format’s length-prefix-on-events discipline (section 8) provides forward compatibility for trace producers: a future format version may add new event payload data without breaking old verifiers, provided the variant discriminants remain unchanged. Larger format changes — adding new variant discriminants, changing the top-level structure — are normative architectural changes that require their own ADR and a version bump. Conforming implementations preserve old format versions' verification capability when bumping the format version, so that traces produced under any historic format remain verifiable. The cross-repo coordination is sequenced: uor-foundation release → prism repin and release → prism-verify follows; any out-of-order release produces a prism build failure (the foundation-side UORassembly check, per ADR-006) and is detected at compile time.

TR-07 — Compile-time content-address stability across toolchain versions

Description: The ContentFingerprint carried by a Certified<GroundingCertificate> is computed by the author’s selected Hasher over data produced by pipeline::run. The data the hasher receives includes the trace’s TraceEvent sequence; the byte representation of the events is stable per the wire format specification (section 8). However, if a future Rust toolchain version changes the layout of intermediate values that contribute to a TraceEvent payload — for instance, if a new toolchain version’s Rust ABI changes the layout of an enum the foundation uses internally — the resulting trace bytes could differ for the same input.

Architectural impact: Bit-identical reproducibility of Grounded<T> and Trace values across toolchain versions is not normatively guaranteed by the wiki. Two builds of the same author’s crate against different toolchain versions could produce traces that verify against the same Hasher but produce different ContentFingerprint values, because the input bytes the Hasher saw were different.

Mitigation posture: The wire format specification (section 8) defines the byte layout of TraceEvent payloads independently of any toolchain version. Conforming implementations of uor-foundation SHOULD produce identical trace bytes across toolchain versions for identical inputs; this is not a normative MUST because it depends on toolchain ABI stability the foundation does not control. Application authors who require cross-toolchain reproducibility pin a specific toolchain version in their crate’s rust-toolchain.toml.

TR-08 — Vocabulary insufficiency in uor-foundation forces cross-repo amendment cadence

Description: prism is closed under uor-foundation's vocabulary (ADR-013): every type prism exposes is derived from uor-foundation's type-declaration vocabulary; every operation declaration composes uor-foundation::PrimitiveOp discriminants. Consequently, a prism amendment that needs a new primitive type (a domain shape uor-foundation's vocabulary cannot express) or a new PrimitiveOp discriminant cannot proceed without first amending uor-foundation. The two crates live in separate repositories (UOR-Foundation/UOR-Framework for uor-foundation; UOR-Foundation/Prism for prism and prism-verify) per ADR-015.

Architectural impact: When prism needs to expand its surface in a way that requires substrate amendment, the amendment cadence is sequenced: identify the substrate gap; amend uor-foundation; release uor-foundation; repin prism; amend prism; release prism. Each step is a cross-repo coordination point. The window between the uor-foundation release and the prism release is one in which prism consumers see a uor-foundation whose vocabulary is wider than what prism exposes; this is benign (the wider vocabulary is just unused in prism's surface) but is observable.

Mitigation posture: The substrate is designed to be wide enough that most prism amendments are additive — they expose a new combination of existing uor-foundation vocabulary, not a new primitive. The closure property in ADR-013 is the architectural commitment that makes this possible; if prism ever needs to introduce a primitive of its own, that is the signal that the substrate’s vocabulary is insufficient and the proper amendment site is uor-foundation. Substrate stability is the architectural goal; cross-repo coordination is the cost of the substrate being a separate, slower-moving layer.

TR-09 — prism version pin lag against uor-foundation

Description: Per the cross-repo split (ADR-015), prism pins a specific uor-foundation version in its Cargo.toml. When uor-foundation releases a new version (e.g., to add a new primitive per TR-08, or to fix a substrate-level bug), prism does not automatically pick up the new version — the prism repository must be amended to repin against the new release. There may be a window in which the latest uor-foundation release contains an amendment that prism does not yet pick up.

Architectural impact: Application authors who require a uor-foundation amendment that has been released but not yet picked up by prism must wait for the prism repin. Cargo’s feature-unification will not silently bridge this gap: the author depends on prism, which pins uor-foundation@X.Y.Z; a new uor-foundation@X.Y.Z+1 is unreachable from the author’s crate until prism repins. The author cannot work around this by declaring their own uor-foundation@X.Y.Z+1 dependency, because Cargo will reject the version conflict.

Mitigation posture: prism's release cadence is expected to follow uor-foundation's closely for substrate-driven amendments (per TR-08). For substrate-level bug fixes, the prism repository’s CI should track uor-foundation releases and produce a repin PR within a short window. The architectural commitment is that prism follows uor-foundation; the operational discipline is that the follow happens promptly. A prism release that lags uor-foundation by a substantial period is an operational concern, not an architectural one, but it surfaces as the practical bottleneck for application authors awaiting substrate amendments.

TR-10 — Route input/output buffer ceilings rejected oversized payloads (resolved by ADR-060)

Status: Resolved by ADR-060. The risk this entry recorded — pipeline::run_route rejecting inputs/outputs larger than the fixed ROUTE_INPUT_BUFFER_BYTES / ROUTE_OUTPUT_BUFFER_BYTES stack-buffer ceilings — no longer exists. ADR-060 removes both ceilings and replaces the fixed buffers with the source-polymorphic TermValue carrier: route input flows as a borrowed &'a [u8] source (TermValue::Borrowed), and route output flows as Inline (within the foundation-derived inline width), Borrowed, or Stream (unbounded). There is no oversized-payload rejection point; payloads of any size flow through Borrowed/Stream with no copy and no ceiling. This entry is retained for traceability.

Original description (pre-0.5.0, superseded): pipeline::run_route allocated fixed-size stack buffers of capacity <B as HostBounds>::ROUTE_INPUT_BUFFER_BYTES and <B as HostBounds>::ROUTE_OUTPUT_BUFFER_BYTES to serialize the input value and accumulate the catamorphism’s output bytes; inputs/outputs whose MAX_BYTES exceeded the ceiling were rejected at runtime with a PipelineFailure::ShapeViolation. The ceilings were the contrived byte-width caps ADR-060 diagnosed as a stable-Rust workaround (a pinned 4096 literal absent generic_const_exprs) with fictional application-overridability.

Residual concern: ADR-060’s carrier surface is lifetime-parametric; downstream code that previously held route outputs across the (then lifetime-free) catamorphism boundary must restructure to hold the byte source for the needed lifetime or materialize via the alloc-gated TermValue::to_vec(). That migration concern is recorded as TR-23.

TR-11 — Verb-graph cycle detection at compile time may produce confusing error spans for complex implementations

Description: ADR-024’s verb-closure check requires the verb-reference graph through non-recurse operators to be acyclic. The verb! macro performs a per-verb local check at each invocation site; the prism_model! macro performs a final whole-implementation cycle check over the verb-reference graph reachable from the model’s route. A cycle is a closure violation surfaced as a Rust compile error.

Architectural impact: Implementations with many cross-referencing verbs and verb imports (via use_verbs!) produce a verb-reference graph whose cycle structure may be non-obvious from any single verb! invocation. The macro’s compile error must point at a span the author can act on: ideally the verb that closes the cycle, but in practice the error may point at the model declaration (where the whole-implementation check runs) rather than the offending verb pair. Authors with complex verb hierarchies may find diagnosing cycles harder than diagnosing closure-under-foundation-vocabulary errors (which always point at the offending function-call span per ADR-022 D3).

Mitigation posture: The macro implementation should produce structured error output: a primary span at the model declaration (where the cycle was detected) plus a sequence of secondary spans at each verb! invocation in the cycle (so the author sees the cycle’s full path). Foundation does not normatively prescribe the diagnostic format; conformant macro implementations include cycle-path information sufficient for the author to break the cycle. Authors writing complex verb hierarchies use the foundation’s cycle-detection diagnostic format as their disambiguator.

TR-12 — prism_model! macro identifier-resolution order has shadowing implications for verbs

Description: ADR-026’s G3-disambiguation rule specifies the macro’s identifier-resolution order: (1) reserved macro-vocabulary identifiers (parallel, fold_n, tree_fold, first_admit, partition_product, partition_coproduct, hash, recurse, unfold, lift, project, nerve, chain_complex, homology_groups, betti, cochain_complex, cohomology_groups, postnikov_tower, homotopy_groups, k_invariants); (2) lowercase PrimitiveOp identifiers from foundation’s canonical PrimitiveOp enum at any committed snapshot per ADR-022 D3 G3a (post-ADR-053: add, sub, mul, div, mod, pow, xor, and, or, neg, bnot, succ, pred, le, lt, ge, gt, concat — 18 variants); (3) axis-trait method identifiers from the model’s declared AxisTuple per ADR-030; (4) own-implementation verbs from verb!-emitted set; (5) imported verbs via use_verbs!. An identifier matching none is a closure violation. Binary-operator forms per ADR-022 D3 G3b (+, -, *, ^, &, |, , <, >=, >) are recognized at expression syntax level before identifier resolution.

Architectural impact: An implementation that declares a verb! named add (the same identifier as the binary PrimitiveOp::Add) cannot use that verb in route bodies because the resolution order resolves the call to PrimitiveOp::Add first. The author must rename the verb to disambiguate. This is a strict ordering: verbs are resolved later than primitives, never earlier. The risk is that authors who name verbs after intuitive English verbs (add, multiply, compare) collide silently with primitive identifiers and find their verbs unreachable from route bodies.

Mitigation posture: The reserved macro-vocabulary identifiers and the PrimitiveOp identifiers are documented (ADR-022 D3 G3 and ADR-026 G12); authors check their verb names against this list before declaring. The macro MAY emit a warning (not an error, since the call is well-typed and the primitive is reachable) when a verb declaration shadows a PrimitiveOp identifier. Authors writing implementations with overlapping vocabulary use namespace-style verb names (my_add, domain_compare, precision_multiply) to avoid shadowing.

TR-13 — Catamorphism evaluation cost may surprise authors of deeply-nested term trees

Description: ADR-029 specifies per-variant fold-rules for the catamorphism. The fold is structurally recursive: each Term::Application evaluates its args first, each Term::Match evaluates the scrutinee then dispatches to an arm body, each Term::Recurse runs descent-measure-bounded iterations of step bodies, etc. The Term tree’s structural depth determines the call-stack depth of the catamorphism’s evaluation; deeply-nested term trees produce deep recursion in the runtime evaluator.

Architectural impact: Authors writing routes with extensive composition (many nested compose calls, deeply nested tree_fold reductions, Term::Recurse with large descent measures producing many recursive applications of the step body) may produce term trees whose evaluation exhausts the stack on resource-constrained targets (thumbv7em-none-eabihf and similar). Foundation’s #![no_std] posture forbids heap allocation in the carrier path, so the catamorphism’s recursion lives on the stack; stack-overflow on small-stack targets is an architectural concern. The behavior is deterministic (same Term tree → same stack depth) but may not match the author’s intuition about where memory pressure arises.

Mitigation posture: The closure-body grammar’s fold_n form (G14) lowers to either an unrolled chain or Term::Recurse based on <B as HostBounds>::FOLD_UNROLL_THRESHOLD per ADR-037 (ADR-026 G14); above the threshold, recursion is iterative-via-recurse rather than directly nested, which keeps the stack depth bounded. Authors targeting small-stack platforms structure their routes around fold_n and Term::Recurse rather than deep direct composition; foundation’s documentation calls out the trade-off. The architectural commitment is to the catamorphism’s correctness, not to a specific stack budget; conformant implementations document their stack profile against representative term trees.

TR-14 — ψ-residuals discipline rejects naturally search-shaped route bodies at proc-macro expansion

Description: The ψ-residuals discipline per ADR-035 + ADR-056 forbids direct syntactic emissions of Term::FirstAdmit, Term::AxisInvocation, and byte-comparison/concat PrimitiveOp s from the route body’s syntactic surface — the prism_model!-declared route closure body. The SDK’s prism_model! macro walks the route body’s term arena at proc-macro expansion and rejects ψ-residual emissions with a closure-violation error citing the ψ-residual category and the route’s name. Per ADR-056, compound verbs declared via verb! per ADR-024, axis impl bodies per ADR-055, and resolver bodies per ADR-046 are not subject to the walk; they may compose the full substrate vocabulary internally because fold-fusion per ADR-054 inlines them through the catamorphism without exposing residual forms at the route surface. Application authors whose domain logic feels naturally search-shaped (e.g., constraint-satisfaction problems whose admission seems to be a predicate over a value-bytes domain) hit the discipline at compile time and must restructure their route body around the canonical ψ-chain (typically the k-invariants branch per ADR-035) — or factor the search-shaped work into a compound verb the route body calls.

Architectural impact: The discipline is load-bearing for the framework’s verifiability commitment — admission at the route surface is a structural property of the value’s k-invariant signature, not a search predicate over byte values. But authors who reach for first_admit over a parametric domain at the route body, or for hash(input) as a direct route-body call, find themselves rejected at proc-macro expansion. The error message must localize to the offending span (the rejected emission site) and cite the ψ-residual category and the architectural reason; without that, authors face a cryptic "route body emitted a ψ-residual" failure and may miss that the same emission is admissible inside a compound verb or axis impl body the route calls.

Mitigation posture: The SDK’s proc-macro emits structured error output naming (a) which Term variant or PrimitiveOp emission was rejected, (b) the offending span in the route body, (c) the ψ-residual category from ADR-035 (FirstAdmit-enumeration / AxisInvocation-dispatch / byte-comparison / byte-concat), (d) the architectural reason (admission at the typed-iso surface must be structural; the canonical k-invariants branch is the maximum-discriminating witness; resolvers consume the canonical hash axis internally rather than via direct route-body emissions), (e) the recommended restructuring path (move the search-shaped work into a compound verb declared via verb! per ADR-024 — its body may use the full substrate vocabulary; or move axis dispatch into a resolver impl per ADR-046; or, if the domain truly is search-shaped at the route surface, use the substrate’s Term::FirstAdmit outside the route body lane in a conformance-corpus generator or foundation-internal trace-replay context). Foundation does not relax the discipline at the route-body surface for application convenience; the architectural commitment is calibrated against the verifiability commitment per ADR-001 + ADR-019.

Companion to ADR-055 (TR-20): TR-14 is the route-body-syntactic-surface counterpart to ADR-055’s universal substrate-Term verb body discipline on axis bodies (TR-20). Authors whose route logic feels naturally search-shaped restructure by introducing compound verbs that internally use the byte-comparison / byte-concat operations the route’s syntactic surface cannot directly emit; the verb-call site in the route body is a Layer-3 identifier per ADR-024, not a ψ-residual syntactic form. Canonical compound operations (SHA padding, HMAC, Merkle, tensor saturation) are substrate-Term-decomposable under ADR-055 + ADR-056 — the discipline blocks their direct emission at the route-body syntactic surface, not their composition within verb / axis-impl / resolver bodies the route calls.

TR-15 — RESOLVER_ABSENT runtime path is the only mechanism reporting resolver-absent at evaluation time

Description: ADR-036’s NullResolverTuple is the default for the R: ResolverTuple substrate parameter on PrismModel. When the model author omits the R parameter at the impl site (or selects NullResolverTuple explicitly) but the verb body emits a resolver-bound ψ-Term variant per ADR-035, the catamorphism dispatches to the corresponding Null<Category>Resolver<H> impl at evaluation time, which returns a ShapeViolation carrying the https://uor.foundation/resolver/RESOLVER_ABSENT shape IRI and the foundation-internal RESOLVER_ABSENT_DISCRIMINATOR byte. The catamorphism propagates the shape-violation through Term::Try per ADR-022 D3 G9 — the verb body’s outer Term::Try handler may recover the resolver-absent case, but absent that handler the propagation reaches pipeline::run_route's Result<Grounded<Output>, PipelineFailure> as a PipelineFailure::ShapeViolation.

Architectural impact: Resolver-absent is a runtime failure mode, not a compile-time bound check. Authors who emit a Term::Nerve (or other resolver-bound ψ-Term) in their verb body but forget to declare the corresponding resolver field in their resolver! impl find the model compiles successfully (the resolver! macro’s "emit all eight impls" pattern satisfies run_route's where-clause unconditionally) but fails at evaluation time on the first per-value invocation. The diagnostic signal at runtime is the PipelineFailure::ShapeViolation carrying the RESOLVER_ABSENT IRI; from the author’s perspective this is structurally similar to other runtime shape-violations (e.g., capacity-exceeded) but rooted in a missing resolver declaration rather than a value’s runtime properties.

Mitigation posture: The SDK’s verb! and prism_model! macros perform a proc-macro-time analysis paralleling the ψ-residuals walk: each verb body’s emitted ψ-Term variants determine the required resolver categories; if any resolver-bound ψ-Term is emitted and the model declaration’s R is NullResolverTuple (or any concrete ResolverTuple whose declared field set does not cover the required categories), the macro emits a warning (or, optionally per the macro implementation’s configuration, a compile error) naming the missing resolver category and the verb that requires it. The warning is non-blocking by default (since the Null* impls satisfy the type-system contract), but conformant macro implementations include this diagnostic so authors discover resolver-absent at compile time rather than at evaluation time. The architectural commitment is to the runtime RESOLVER_ABSENT mechanism (the foundation-internal byte discriminator + the shape IRI) as the load-bearing path; the proc-macro warning is a diagnostic convenience. Authors building applications under the canonical k-invariants branch declare the four required resolver fields (nerve, postnikov, homotopy_groups, k_invariants) in their resolver! invocation to avoid resolver-absent at evaluation time.

Post-ADR-055 cross-reference: ADR-055’s universal substrate-Term verb body discipline moved axis-body structural-correctness from runtime / inclusion-criterion to compile-time (TR-20). Resolver-absent retains its runtime mechanism — the resolver-tuple selection is application-author state independent of axis bodies, and the RESOLVER_ABSENT ShapeViolation is structurally the same propagation path as other runtime shape-violations per ADR-022 D3 G9. The proc-macro warning is the architecturally-recommended convenience for moving resolver-absent detection to compile time where verb-body emission analysis admits it.

TR-16 — Canonical hash axis selection may fail σ-Projection Hardening Principle conformance

Description: ADR-047 commits the σ-Projection Hardening Principle (U1–U6 axioms) as substrate-level qualification criteria for Hasher impls per ADR-030. ADR-049 provides the empirical witness through uor_foundation::axis::cryptanalyze::<H: Hasher>(samples) → CryptanalysisReport at samples = 10^7. Application authors selecting a Hasher impl from any source — foundation’s DefaultHasher, prism-crypto’s published impls per ADR-031, or a third-party axis crate — implicitly stake the framework’s verifiability commitment (per ADR-001 + ADR-019) on that impl satisfying U1–U6. An impl that fails any axiom (the most common failure mode: U3 admission-orthogonality failing for a structurally-weak hash function) introduces a cheaper-than-σ observable predicting admission, defeating ADR-035’s commitment to "one structural inference per invocation" — applications using such an impl could shortcut the typed-iso surface by reading the admission-correlated observable directly.

Architectural impact: The Hardening Principle is a qualification criterion, not a runtime check; a non-conforming Hasher impl compiles successfully (the trait surface is satisfied) but exposes the framework’s verifiability commitment to undetected structural attack. Applications consuming a non-conforming impl produce Grounded<Output> values whose admission claim is structurally weaker than the typed-iso surface’s 2^K × α^-1 PRF-baseline cost contract per ADR-048’s U6 bandwidth-additivity — the K-bit conjunction’s cost becomes less than 2^K × α^-1 (the structural attack reduces it), violating the cost-identity equality. Authors selecting an unvetted impl cannot detect the non-conformance from the trait surface alone.

Mitigation posture: Foundation’s CI runs axis::cryptanalyze at samples = 10^7 against DefaultHasher and the prism-crypto-published Hasher impls per ADR-031 on every release; conformance failure blocks the release. Third-party axis crates SHOULD declare U1–U6 conformance in their crate-level documentation with a reproducible witness (a cryptanalyze invocation in the crate’s CI). Application authors selecting a third-party Hasher impl SHOULD run axis::cryptanalyze against the selected impl at application development time and pin the report’s pass/fail verdict in their CI; the report’s structured form makes the pin auditable. The architectural commitment per ADR-047 is to the U1–U6 axioms as the substrate-level qualification criterion; the empirical battery per ADR-049 is the verifiable witness. Authors selecting a non-conforming impl assume the structural risk.

TR-17 — Deeply-nested AndCommitment<A, B> chains may slow proc-macro expansion and downstream codegen

Description: ADR-048’s AndCommitment<A: TypedCommitment, B: TypedCommitment> carries the typed-bandwidth conjunction structure at the type level. Each .and() call constructs a new type at compile time; a K-fold conjunction becomes a nested type AndCommitment<AndCommitment<…​AndCommitment<P_1, P_2>, …​>, P_K> of depth K. The proc-macro expansion of prism_model! per ADR-022 D3 parses the optional fifth C: TypedCommitment clause and emits the trait impl’s forward body with the threaded &C reference; the trait-resolver in rustc must monomorphize the chain for the application’s specific commitment selection. At large K (the cryptanalysis battery in ADR-049 tests up to K=12; applications declaring custom bandwidth commitments may push higher), the trait-resolver’s work compounds, and compile times may grow non-linearly.

Architectural impact: The architectural commitment per TC-01 + ADR-019 is to zero-cost monomorphized typed-bandwidth admission — at runtime, the AndCommitment chain unrolls to inlined predicate evaluations. The compile-time cost is the architectural trade-off: large K shifts work from runtime to compile time. The trait-resolver’s work is monomorphization, not type-checking, so the work is bounded but may dominate compile times for applications with large K + large PrismModel parameter spaces (the trait-resolver is sensitive to the cross-product of substrate-parameter selections + commitment depth).

Mitigation posture: Application authors who need K-fold conjunctions with large K (K > 32, say) factor their commitment into shallower sub-commitments at architectural boundaries — for example, two AndCommitment chains of depth K/2 named under typed aliases the macro consumes as opaque types. Foundation provides no fixed K ceiling architecturally; no MAX_AND_COMMITMENT_DEPTH substrate parameter exists. The architectural commitment is to the zero-cost runtime contract, not to a specific compile-time profile; conformant implementations document their compile-time behavior at representative K. The Hardening Principle’s U6 axiom per ADR-047 guarantees the runtime cost identity holds regardless of K — 2^K × α^-1 expected trial count per κ-label, with no degradation as K grows. The compile-time cost is a known trade-off authors manage through their architectural decomposition.

TR-18 — Width-parametric arithmetic fold-rules cold path may surprise authors at wide Witt levels

Description: ADR-050 commits the ring-axis and hypercube-axis arithmetic PrimitiveOp s — Add, Sub, Mul, Div, Mod, Pow, Neg, Bnot, Succ, Pred, Xor, And, Or — to evaluate at the full Witt tower up to the application’s WITT_LEVEL_MAX_BITS (the inline carrier admits these via carrier_inline_bytes::<B>() per ADR-060, which includes the Witt-literal width WITT_LEVEL_MAX_BITS / 8). The fold-rules' u64 fast path applies at result_width ⇐ 8; wider widths up to WITT_LEVEL_MAX_BITS / 8 use byte-wise carry-based evaluation (ripple-carry add; schoolbook multiply; binary long division; square-and-multiply pow). The per-op cost at wide widths is bounded above by O(width) for additive ops, O(width^2) for Mul, O(width^2) for Div/Mod, and O(width^2 · log(exp)) for Pow. Authors composing arithmetic at wide widths (256-bit, 512-bit, etc.) may not anticipate the quadratic cost of Mul/Div/Mod relative to the linear cost of Add/Sub.

Architectural impact: The cost is structural — every conformant implementation realizes the fold-rule at the operand width per ADR-050’s strategy-independent semantics; the byte-output is identical across implementations regardless of algorithm choice. But the wall-clock cost at e.g. result_width = 256 is empirically ~32x linear for Mul over the u64 fast path. Authors writing verbs that compose Mul/Div/Pow at wide widths may produce binaries whose per-invocation cost exceeds the cost-model’s 2^K × α^-1 baseline per ADR-047 U6 — the baseline assumes per-invocation cost is O(1) plus the K · t_eval predicate evaluations, not O(width^2). At sufficiently wide widths the per-invocation cost dominates the bandwidth scaling.

Mitigation posture: Authors targeting wide arithmetic widths SHOULD profile representative verb-body compositions at the application’s selected HostBounds::WITT_LEVEL_MAX_BITS and confirm the per-invocation cost remains within their performance budget. The architectural commitment is to byte-output correctness at every admitted Witt level; performance characterization is application-specific. Implementations MAY apply algorithmic optimizations within the strategy-selection lane per ADR-026 (e.g., Karatsuba multiplication for wide Mul, Barrett reduction for repeated modular operations, sliding-window for Pow) — these are byte-output-equivalent to the reference impl and admissible. Each application declares its own WITT_LEVEL_MAX_BITS per ADR-060 (there is no DefaultHostBounds); authors selecting larger algebraic-level ceilings accept the proportional cost.

TR-19 — Div/Mod zero-divisor runtime path surfaces only at evaluation time

Description: ADR-053 commits Div(a, 0) and Mod(a, 0) to emit a ShapeViolation::ValueCheck carrying the constraint IRI https://uor.foundation/op/Div/nonZeroDivisor (or the corresponding op:Mod IRI). The check is runtime per the catamorphism’s per-variant fold-rule evaluation; the b = 0 case is not detectable at proc-macro expansion because the operand value is determined at evaluation time. The ShapeViolation propagates through Term::Try per ADR-022 D3 G9 to pipeline::run_route's Result<Grounded<Output>, PipelineFailure> as Err(PipelineFailure::ShapeViolation).

Architectural impact: Authors composing arithmetic involving division — field_inv<P>(a) = Div(1, a) where a might be the additive identity in Z/PZ; modexp_p(a, b, p) = Mod(Pow(a, b), p) where p might be zero through a malformed input — find their verb compiles successfully but fails at evaluation time on inputs that present zero divisors. The failure path is correct (the ShapeViolation carries the operation IRI and the offending Term arena position), but authors may not anticipate that division semantics surface as a runtime envelope rather than a compile-time bound check.

Mitigation posture: Authors writing arithmetic verbs SHOULD compose the b > 0 precondition explicitly through the closure-body grammar — for example, wrapping Div(a, b) inside a Term::Match per ADR-022 D3 G6 that dispatches on b, or using first_admit over the input’s structural metadata to filter zero-divisor inputs before the division. The architectural commitment per ADR-053 is to the runtime ShapeViolation as the load-bearing signal; the proc-macro-time check is admissible only when b's value is constant-foldable at the typed-iso surface (e.g., Div(a, 7) where 7 is a Term::Literal { value: 7, …​ } — the proc-macro MAY emit a warning or compile error for Div(a, 0) with literal-zero divisor, but the architectural commitment is to runtime detection). Foundation’s reference impl’s Div/Mod fold-rule check is unconditional at evaluation time; implementations MAY add proc-macro-time checks as additional diagnostic surface.

TR-20 — Axis impl body discipline regression detected at compile time, not runtime

Description: ADR-054 originally committed the canonical axis impl body discipline as a standard-library-only inclusion criterion per ADR-031 — application-author custom axes retained ADR-010’s permission for arbitrary-Rust bodies and were not bound by the discipline. ADR-055 supersedes this scope: the discipline is now universal and type-system-enforced via the foundation-declared SubstrateTermBody sealed supertrait on AxisExtension. Every axis impl (standard-library AND application-author custom) MUST provide a body_arena() static Term slice the catamorphism walks; impls that do not satisfy the trait bound fail to compile. Fold-fusion’s structural reach through the axis surface to the leaf level is therefore architecturally guaranteed for every axis impl, not provisional.

Architectural impact (post-ADR-055): Discipline regression is no longer a runtime / inclusion-criterion concern but a compile-time error. An axis impl that attempts to declare an arbitrary-Rust kernel body without a SubstrateTermBody impl fails to satisfy the AxisExtension supertrait bound; the application crate fails to compile. The risk is shifted from "runtime structural-reach failure undetectable from the trait surface" (the pre-ADR-055 framing) to "compile-time error with a clear diagnostic." Application authors targeting domains for which substrate-Term decomposition is unknown discover this at the axis! invocation site, not at evaluation time.

Migration risk for pre-ADR-055 axes: Application authors with pre-ADR-055 axis impls whose kernels were arbitrary-Rust bodies face a migration choice per ADR-055’s two-option path: (1) decompose the kernel to a substrate-Term composition via the closure-body grammar (the universal path; per ADR-019’s initial-algebra commitment, every prism operation is substrate-Term decomposable); (2) move the off-substrate kernel out of the axis surface and into a host-boundary wrapper around forward(input). Option 2 places the operation outside the catamorphism’s reach where it always conceptually was; the framework’s verifiability commitment does not extend to it. Pre-ADR-055 authors who attempt to compile their existing axis impls against post-ADR-055 foundation see a clear diagnostic naming the missing SubstrateTermBody impl with a pointer to the SDK macro’s body clause.

Mitigation posture: Foundation declares the SubstrateTermBody trait per ADR-055 with bounded compile-time diagnostics: the trait surface is documented; the axis! SDK macro emits both AxisExtension and SubstrateTermBody impls together when the body clause is provided; absent the body clause, the macro emits a compile error with explicit guidance ("axis impls MUST provide a body clause per ADR-055; refer to closure-body grammar G1–G29 for the substrate-Term vocabulary"). The conformance suite per ADR-031 includes byte-equivalence checks between the substrate-Term verb body and the canonical reference vectors per ADR-054’s conformance commitment (FIPS-180-4 for SHA-2, FIPS-202 for SHA-3, the BLAKE3 spec for BLAKE3, the secp256k1 SEC 2 §2.4.1 vectors for prime-field, BLAS reference outputs at integer precision for tensor, the FHE-scheme correctness predicates for prism-fhe). Foundation’s CI runs the conformance suite on every release; third-party Layer-3 crates inherit the type-system enforcement automatically — no operational discipline-audit is required because the SubstrateTermBody bound rejects non-conforming impls at compile time.

TR-21 — Shape-IRI registry capacity and verifier-side registry drift

Description: ADR-057 introduces foundation’s shape_iri_registry module collecting ConstrainedTypeShape impls at link time via the register_shape! SDK macro. The registry is consulted by ψ_1 NerveResolver at evaluation time to resolve ConstraintRef::Recurse.shape_iri references. Two architectural concerns are calibrated against this mechanism: (a) the prism-side registry built into the application binary and the verifier-side registry built into prism-verify MUST reflect the same registered set when both link against the same foundation version — registry drift between the two sides could surface as a verifier-time Recurse-target-not-found shape violation on a constraint set the prism-side admitted; (b) the registry’s capacity is foundation-vetted (the link-section collector has no per-application ceiling; the architectural commitment is that the registry’s size scales with the application’s declared shape vocabulary, not with the substrate’s discriminator caps).

Architectural impact: The wire-format version constant bump per ADR-013/TR-08 is the explicit synchronization mechanism — verifiers reject traces whose wire-format version constant indicates a different substrate version than the verifier’s. Within a fixed wire-format version, the registry’s shape-IRI set MUST be identical between prism and prism-verify; the architectural commitment is to link-time symmetry under the closure rule (every shape registered prism-side is registered verifier-side, by virtue of both crates linking against the same foundation and using the same register_shape! macro expansions). Cross-crate registration is admissible — application crates and standard-library Layer-3 sub-crates per ADR-031 may both contribute shapes — but the link-time collection must include every shape the application’s constraint geometry references. A ConstraintRef::Recurse reference to an unregistered shape-IRI surfaces as a runtime ShapeViolation carrying the proof:InhabitanceImpossibilityWitness shape IRI per ADR-039 at ψ_1 NerveResolver dispatch.

Mitigation posture: The SDK’s partition_coproduct! / partition_product! macros emit the register_shape! call automatically alongside any expansion that uses recurse(<bound>):<T> operand markers per ADR-057, so the referenced shape is guaranteed registry-present before evaluation can reach the Recurse reference within a single crate’s compilation unit. Cross-crate registration relies on the prism-verify build process linking the same crate graph as the application’s prism build — applications that exercise verifier-side replay through prism-verify MUST build the verifier-side binary against the same dependency graph that contributed the registry’s content. Foundation’s conformance suite per ADR-031 includes a registry-symmetry check verifying that the prism-side and verifier-side registries contain the same shape-IRI set after both binaries are built; failure surfaces as a substrate-level non-conformance signal at conformance-test time, not at end-user replay time. Cycle-detection at evaluation time is structurally guaranteed by the descent-budget mechanism: every Recurse reference carries a descent_bound: u32, and the unrolling decrements the budget on each reference encountered along the walk; descent_bound = 0 terminates the recursion regardless of the shape-IRI reference graph’s cycle structure. Mutually-recursive shape graphs are admissible per ADR-057’s registry closure rule (c) as long as every reachable cycle is bounded by some descent_bound at every reference instance along the cycle.

TR-22 — Atlas / exceptional-structures codomain typing is conceptually committed but code-realization-deferred

Description: ADR-059 commits the Atlas image inside E₈ — with the Hopf convergence tower per kernel::convergence as coarse stratification and the exceptional algebraic structures (G₂ / F₄ / E₆ / E₇ / E₈) as finer-grained typing — as the codomain of κ-derivation in operator-geometry coordinates. This is a conceptual-reading commitment (ADR-059 §6). The convergence tower itself is realized in the substrate (kernel::convergence is @generated ontology-derived foundation vocabulary), but ADR-059 §6 + rejected-alternative-7 explicitly defer the Atlas-of-Resonance-Classes and exceptional-structures code realization to application level: "an exact-rational-arithmetic library admitted as a Layer-3 sub-crate per ADR-031’s demand-driven inclusion clause, or … a sibling library consumed by application code." The wiki normatively specifies the codomain structure; the code realization of the Atlas → E₈ embedding and the categorical-operation derivations of G₂ / F₄ / E₆ / E₇ is not part of uor-foundation and may not ship in any standard-library Layer-3 sub-crate at a given time.

Architectural impact: An application that wants a C: TypedCommitment admission predicate referencing the finer-grained codomain typing per ADR-059 §5 — Atlas-image proximity, exceptional-group Weyl-orbit membership, or a convergence-tower level-and-residual signature beyond the coarse R / C / H / O stratification — depends on a code realization of the Atlas / exceptional structures that may not be present. The coarse convergence-tower stratification (R / C / H / O level + ConvergenceResidual signature) is substrate-native via kernel::convergence and always available; the finer Atlas / exceptional-group typing is code-realization-contingent. The framework’s verifiability and content-addressing commitments per ADR-001 + ADR-017 + ADR-019 do not depend on the Atlas realization — the κ-label (byte-coordinate) and the canonical k-invariants branch (T1/T2/T3 closure-lossless taxonomy per ADR-058) are framework-native and require no exceptional-structures library. The risk is scoped to applications electing the operator-geometry-codomain-typed admission vocabulary per ADR-059 §5, not to the framework’s core commitments.

Mitigation posture: ADR-059 is a conceptual reading; its byte-coordinate dual (the κ-label per ADR-058 + ADR-047) is always realized, so applications needing only T1/T2/T3 equivalence or coarse convergence-tower stratification incur no dependency on the Atlas realization. Applications needing the finer codomain-typed admission vocabulary await the exact-rational-arithmetic library’s contribution under ADR-031’s demand-driven inclusion clause (or supply their own sibling realization); per ADR-031 the architecture treats such a library identically whether it is a standard-library sub-crate or a third-party crate, so the codomain-typed admission predicates are expressible as soon as any conforming realization is on the dependency graph. The wiki’s normative codomain-structure specification is stable regardless of where or when the code realization lands; ADR-059 §6 fixes the conceptual commitment independently of the realization’s location, so no wiki amendment is required when the realization ships.

TR-23 — Source-polymorphic carrier’s lifetime parameter migrates downstream call sites

Description: ADR-060 replaces the fixed-buffer TermValue with a source-polymorphic, lifetime-parametric, const-generic carrier (TermValue<'a, const INLINE_BYTES: usize>), and the catamorphism’s evaluate / pipeline::run_route gain the 'a input-source lifetime and the INLINE_BYTES const-generic argument. Borrowed and Stream carriers reference an upstream byte source whose lifetime the catamorphism propagates; a carrier cannot outlive the source it references. Downstream code written against the pre-0.5.0 lifetime-free TermValue that held a carrier (or a Grounded output value) across the catamorphism’s boundary will not compile against the 0.5.0 surface without restructuring. ADR-060 supplies no evaluate_owned() fallback (rejected-alternative-6) — a foundation-level owning mode would re-introduce allocation on the principal data path against the #![no_std]-without-alloc posture.

Architectural impact: The migration is a compile-time concern, not a runtime one: the borrow checker rejects a carrier held beyond its source’s lifetime. Two restructurings discharge it — (a) hold the byte source itself for the lifetime the carrier is needed (the zero-copy path), or (b) materialize the Borrowed/Stream carrier into an owned Vec<u8> via the alloc-gated, opt-in, per-caller TermValue::to_vec() helper (the only allocation surface; not a foundation-level mode). Resolver impls whose scratch does not outlive the carrier they construct must either arrange longer-lived scratch (e.g., a caller-supplied bump arena) or materialize via the alloc-gated path before the scratch goes out of scope. Authors accustomed to the lifetime-free carrier may initially read the borrow-checker errors as unexpected.

Mitigation posture: The lifetime parameter makes explicit a dependency that pre-0.5.0 code carried implicitly (the catamorphism’s outputs always referenced byte sources whose validity the caller had to ensure); the borrow checker now enforces it. The const-generic INLINE_BYTES argument is materialized once at the application boundary as carrier_inline_bytes::<MyBounds>(), propagated by the prism_model! / axis! / verb! / prism_resolver! macro emissions, so application authors using the macro surface do not write the parameter by hand. Authors using the foundation/SDK directly thread the parameter at the evaluate / resolver-impl boundary. The alloc-gated to_vec() helper provides a mechanical escape for code that genuinely needs an owned buffer, keeping the no-alloc principal data path intact for code that does not. The architectural commitment is the source-polymorphic carrier; the lifetime threading is its type-system expression, not an incidental cost.

Clone this wiki locally