Residual Core R0-C2 Contract
Status: implemented and validated
Stage: Roadmap Stage 2 / Residualization R0-C
Decision: ADR-0027
Scope: bounded folding and aggregate Residual Core generation from an opaque
R0-B2 evaluation record
1. Boundary
R0-C2 consumes exactly:
ValidatedSpecializationRequest
opaque MixedStaticEvaluation produced by evaluate_static_r0b2
The evaluation record exposes read-only observations. External Rust callers cannot construct or mutate its request hash, outcome, trace, steps, or skipped nodes. The generator also rejects a record whose request hash differs from the validated request.
This closes the in-process same-request mutation path present in the original R0-C1 API. It is not the independently serialized and replayed R0-D evidence boundary.
2. Deterministic transformation
R0-C2 leaves generate_residual_r0c frozen as the R0-C1 regression path and
adds generate_residual_r0c2.
The R0-C2 generator:
- narrows the entry signature to dynamic slots;
- replaces an R0-B2 static
Letfact when the residual still reads it; - removes a proven static binding when all residual reads disappear;
- folds an
Ifwhose condition resolves to a staticBool; - folds a
Casewhose scrutinee resolves to a staticSum; - binds fields of the selected constructor before the selected arm;
- eliminates a direct call when its result binder has an R0-B2 fact;
- retains calls whose result remains dynamic;
- walks the post-folding call graph from the entry and retains only reachable functions, preserving original FunctionId order.
No general syntactic inlining, alpha-renaming, speculative rewriting, trace guard, or profile feedback is used.
3. Aggregate materialization
Unit, Bool, I64, and F64 use canonical Core operands.
Live Tuple and Sum values are rebuilt as existing Core Let bindings:
nested children before parents
fields in source/type order
fresh LocalIds increasing from max(source entry LocalId) + 1
An existing parameter, Let, or selected Case-field LocalId is reused for the
root value. A complete aggregate result receives a fresh root LocalId.
LocalId exhaustion fails closed.
P1V0 has no array literal. A static Array<F64> is therefore accepted only
when transformation removes every runtime read. If a residual return,
primitive, call, aggregate, or other term still needs the value, generation
fails with UnsupportedLiveStaticValue.
4. Effect and control preservation
Only values actually established by R0-B2 may erase their source binding. Dynamic, denied, unavailable, or otherwise skipped work is preserved.
A Complete result collapses to a constant only when R0-B2 reported no
skipped nodes. When skipped work exists, R0-C2 retains that work and may only
fold control justified by static entry slots. Function effect rows remain
conservative declarations and are never narrowed by R0-C2.
5. Admission gates
Before an artifact is returned:
- the opaque evaluation record must match the validated request;
- every live static value must have an exact Core representation;
- fresh LocalId allocation must remain in the
u32namespace; - the whole residual program must fit
max_residual_nodes; - canonical semantic bytes must fit
max_residual_bytes; CoreArtifact::sealmust succeed;- the ordinary Core verifier must accept the artifact.
Any failure returns no residual artifact.
6. Evidence
naux-lang/tests/residual_r0c2.rs locks:
- static
Iffolding and removal of an unselected checked-effect branch; - static
Casefolding and selected-field materialization; - a live static Tuple rebuilt for a retained dynamic call;
- deterministic child-first nested Tuple/Sum result materialization;
- static-array elimination after
ArrayLenF64specialization; - fail-closed rejection of a live static array;
- static-call elimination and unreachable-callee pruning;
- preservation of skipped effectful work despite a complete static result;
- exact LocalId exhaustion behavior;
- residual node and byte budget rejection;
- original-versus-residual result and effect-trace parity across dynamic input vectors.
The focused R0-B2/R0-C1/R0-C2 corpus and the complete workspace test suite pass after this boundary. Clippy with warnings denied passes for the affected library and integration targets.
7. Non-claims
R0-C2 is a verified-output generator, not independent residual evidence. It does not implement R0-D, CoreVM0, standalone native code, Futamura P1/P2, Projection Birth, dependency closure, or Nauxogenesis.
The next gate is R0-D: canonical residual evidence plus an independent validator that does not trust generator-owned transformation claims.