-
Notifications
You must be signed in to change notification settings - Fork 12
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.
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.
Description: pipeline::run_route allocates fixed-size stack
buffers of capacity <B as HostBounds>::ROUTE_INPUT_BUFFER_BYTES (per
ADR-023 reconciliation, migrated to HostBounds per ADR-037) and
<B as HostBounds>::ROUTE_OUTPUT_BUFFER_BYTES (per ADR-028, migrated to
HostBounds per ADR-037) to serialize the input value (via
IntoBindingValue::into_binding_bytes) and to accumulate the
catamorphism’s output bytes (per ADR-029). Inputs whose
<M::Input as IntoBindingValue>::MAX_BYTES exceeds the input ceiling,
or models whose <M::Output as IntoBindingValue>::MAX_BYTES exceeds the
output ceiling, are rejected at runtime with a
PipelineFailure::ShapeViolation. The ceilings are HostBounds
associated constants per ADR-037; applications declare their own
HostBounds impl to raise the ceilings for their typed feature
hierarchies, or select DefaultHostBounds which carries foundation’s
prior fixed values as defaults.
Architectural impact: An application whose domain requires inputs or
outputs larger than the foundation-fixed ceilings cannot run on the
current foundation release; the author must either restructure the
application’s input/output shapes to fit under the ceilings (e.g.,
compose multiple smaller models that pipeline through
partition_product shapes) or wait for a foundation parameter change to
raise the ceiling. The runtime-rejection point is the buffer-size check
inside run_route, which is later than the type-system would catch the
analogous violation under generic_const_exprs; the failure is
structurally identical (buffer too small) but is relocated from
compile-time to runtime under the stable-Rust form (per ADR-023 Rejected
alternative 4).
Mitigation posture: The architectural commitment is to the
foundation-fixed ceiling form, not to specific numeric values.
Foundation chooses ceiling values that accommodate typical applications
(typical input/output shapes well under the ceiling); applications with
outsized shapes flag this as a foundation parameter request rather than
a workaround. The structural commitment that the ceilings exist is
deliberate: it bounds the executable’s stack usage at compile time,
which is required by the #![no_std] posture and the zero-cost runtime
constraint. The risk surfaces as a one-time application-foundation
coordination cost when an application’s shapes exceed the ceiling.
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 (foundation 0.3.6: add, sub, mul, xor, and,
or, neg, bnot, succ, pred, le, lt, ge, gt, concat);
(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: ADR-035’s ψ-residuals discipline forbids
Term::FirstAdmit, Term::AxisInvocation, and byte-comparison/concat
PrimitiveOp emissions in verb-body terms. The SDK’s verb! and
prism_model! macros walk the verb body’s term arena at proc-macro
expansion and reject ψ-residual emissions with a closure-violation error
citing the ψ-residual category and the verb’s name. 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 verb body around the canonical ψ-chain (typically the
k-invariants branch per ADR-035) instead.
Architectural impact: The discipline is load-bearing for the
framework’s verifiability commitment — admission 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, or for hash(input) as a verb-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 "verb body emitted a ψ-residual" failure.
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 verb body, (c) the ψ-residual
category from ADR-035 (FirstAdmit-enumeration / AxisInvocation-dispatch
/ byte-comparison / byte-concat), (d) the architectural reason
(admission must be structural; the canonical k-invariants branch is the
maximum-discriminating witness; resolvers consume the canonical hash
axis internally rather than verb-body terms), (e) the recommended
restructuring path (move axis dispatch into a resolver impl; express
admission as a ψ-Term composition over the value’s structural
decomposition; if the domain truly is search-shaped, use the substrate’s
Term::FirstAdmit outside the verb-body lane in a conformance-corpus
generator or foundation-internal trace-replay context). Foundation does
not relax the discipline for application convenience; the architectural
commitment is calibrated against the verifiability commitment per
ADR-001 + ADR-019.
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.
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.