-
Notifications
You must be signed in to change notification settings - Fork 15
11 Technical Risks
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Generated from sources at UOR-Framework.wiki. Do not edit pages directly via the GitHub web UI — edits are overwritten by the next build. See README for the authoring workflow.