Prepared Project Interpreter and Source Trace v1

September 12, 2026 · View on GitHub

Status: implemented bounded profile; HOSTED GREEN under the v0.4.0 release baseline. Historical local, authoring-time, ignored, or separately provisioned observations below retain their narrower scope; public promotion and broader product completion remain separately gated.

Audience: language-tool authors, agent builders, and compiler contributors.

This additive library lane prepares the exact entry and test closures of one immutable ProjectRevision once, then evaluates them sequentially on one long-lived fixed 64 MiB stack. It does not parse, resolve, link, scan closure admission, or allocate another evaluator thread for each request. Existing ProjectRevision::execute_*, semaprax.project-execution.v1, Interpreter v1, and Project Agent Transport v1-v5 APIs and bytes are unchanged.

The explicit Prepared Project Revision Replacement v1 operation can subsequently replace that worker's subject without replacing the worker. It requires an exact expected current revision, prepares both new closures before the handoff, and preserves the old subject on ordinary rejection. It does not change this lane's evaluation or trace protocol.

Preparation independently validates both retained HIR programs, replays the explicit-entry/signature/profile closure gate, and creates an owned stable-ID to function-index map for the exact transitive closures. The combined cache is limited to 262,144 expression origins and 16 MiB of identity/source-index facts. Each expression span must fit its authenticated Project source and one complete origin fact must fit the minimum output envelope. A prepared value is not cloneable and owns exactly one sequential worker.

Library surface

ProjectSnapshot::prepare_interpreter clones only its authority-neutral Arc<ProjectRevision>. The same operation is available on Arc<ProjectRevision> and through prepare_project_interpreter. Preparation takes PreparedProjectInterpreterOptions, whose byte/event ceilings bound all later requests.

PreparedProjectInterpreter::execute_entry, execute_test, and execute accept PreparedProjectExecutionOptions and one monotonic ProjectExecutionCancellation. The cancellation handle is an in-process atomic flag. It has no reset, deadline, clock, forced preemption, or transport meaning. Cancellation is observed before a charged evaluator node and is reported as cancelled { before_step }; a pre-cancelled request therefore uses zero fuel and reports boundary one. Without cancellation, evaluation order, fuel, normalized failures, call-depth behavior, and returned values use the same evaluator as the legacy Project route.

Explicit untraced execution

The additive execute_untraced(role, max_steps, cancellation), execute_entry_untraced(max_steps, cancellation) and execute_test_untraced(max_steps, cancellation) methods return UntracedPreparedProjectExecution: role, normalized outcome, steps used and step ceiling, with no trace or evidence envelope. They reuse the same worker, fail-fast admission, retained closure checks, evaluator, cancellation and revision replacement. Invalid fuel bounds still fail SPX-F108.

This explicit API selects the evaluator's existing zero-event mode. During execution it allocates no event vector storage, interns no trace identities, and performs no trace rendering. Preparation still builds the admitted closure and source-origin indexes, so a caller can alternate traced and untraced runs. Existing traced methods, positive trace bounds, defaults and canonical trace bytes remain unchanged. Traced execution after an untraced request has the same bytes for the same revision, options and observed cancellation boundary.

This additive API has focused local evidence; it is not covered by the older release baseline cited above. It enables measuring tracing overhead but makes no speedup claim. Legacy retained execution additionally publishes its Project execution report; a no-trace result alone does not make all benchmark products identical.

BoundValue
maximum evaluator steps100,000,000
call depth256
trace bytes65,536–16,777,216
trace events1–65,536
prepared origin nodes262,144
prepared index content bytes16,777,216
retained workers per process8
execution/queued request per workerone/zero; concurrent calls fail SPX-F109

Defaults are 1,000,000 steps, 1 MiB, and 4,096 events. Once the event ceiling or byte budget is reached, evaluation continues and the deterministic prefix records exact recorded_events, dropped_events, and truncated facts. Trace saturation never changes the language outcome. Prefix selection performs one bounded linear scan: source strings and event JSON are retained only for events that fit, and the renderer never repeatedly reconstructs oversized prefixes. The index-byte ceiling accounts canonical identity and source-fact content; the separate node and worker ceilings bound implementation overhead without claiming allocator-specific heap-byte equality.

Source Trace v1

semaprax.project-source-trace.v1 uses the exact outer wrapper schema,digest,bytes,payload; the digest domain is semaprax.project-source-trace.payload.v1\0. Normatively, the digest is SHA-256(domain || little_endian_u64(payload_byte_length) || exact_payload_bytes). Its wire form is the lowercase text sha256: followed by exactly 64 lowercase hexadecimal digits. The payload binds Project schema, Project/Workspace revisions, Project graph digest, closed entry/test role, module and stable entry identity, limits/fuel/outcome, ordered events, truncation, and fixed nonclaims.

Each event records event index, charged fuel step, call depth, phase (requires, body, or ensures), persistent function ID, revision-scoped expression ID, logical Project path, exact source revision/digest, and byte/ line/column span. It includes no source excerpt. Outcomes are returned i64, compiler-owned language failure, fuel exhaustion, call-depth exhaustion, or cooperative cancellation.

verify_project_source_trace checks the closed JSON shape, canonical values, bounds, status vocabulary, byte count, digest, and exact reconstruction. verify_project_source_trace_against_revision additionally binds every event to an expression in the exact transitive retained closure, its structural requires/body/ensures phase, and its authenticated source fact. V1 does not independently re-execute the dynamic path; its public digest detects byte drift but is not producer authentication, while the revision verifier proves bounded closure/source-origin consistency.

Diagnostics are closed: SPX-F107 preparation/source-index admission, SPX-F108 request bounds, SPX-F109 worker lifecycle/panic, and SPX-F110 trace rendering or replay.

Authority and nonclaims

The worker is a reference interpreter, not target execution, a JIT, debugger, profiler, or sandbox. SEMAPRAX code gains no filesystem, process, environment, network, clock, backend, publication, or mutation authority. Atomic cancellation timing is not schedule-deterministic; only its observed semantic step boundary is canonical. Trace bytes are not provenance, approval, compatibility, target, or production evidence. A live ProjectSnapshot caller must continue to place work inside its existing held-input pre/post authentication boundary.

Focused authored evidence is in src/project/prepared_interpreter/tests.rs. Promotion additionally requires legacy Project/Interpreter and Agent Transport byte preservation, runtime parity, hostile trace replay, capacity boundaries, worker fail-stop, held-input drift suppression, and exact-head hosted execution under the ordinary quality policy.