Complete generation-atomic input and output frames
Status and authority
This is the normative detail of PC-031 in the product contract. Preparation (PC-032), complete-frame execution (PC-033), shared evaluation core (PC-034), and frame-only facade contraction (PC-035) are implemented. The APIs below supply no profile selector or stable-product guarantee. The separate replay record defines the canonical per-accepted-frame encoding. Execution maintainers own these semantics; host policy remains with the host integrator.
The requirements below describe the sole public execution path. Legacy execution methods have been removed, not reinterpreted or aliased. “Atomic” means an engine-owned transition under the ordinary returned-refusal boundary below, not a distributed, persistent or actuator transaction. The fixed HostTick v1 profile remains unchanged.
Determinants and executable boundary
A transition is determined by the loaded executable (including effective parameters, schedule and value domains), compatible entry execution state, the fixed execution profile, model time, and the complete input values. Completeness closes the external-input determinant set; it does not make a stateful transition independent of its prior state or imply unrestricted cross-build determinism.
- Every executable boundary input MUST be supplied exactly once unless the executable schema explicitly declares an engine-level default or optionality with a deterministic omission meaning. An absent declaration means required. An optional input, when supplied, is still subject to uniqueness and type validation. An executable with no boundary inputs admits an empty value set only after the other acceptance checks pass.
- Completeness is over canonical executable input identities, not every row marked input in today’s point inventory, and not the union of all internal connector slots. One boundary identity may fan out to several consumers; the host supplies it once and the engine resolves all its targets. Internal driven connectors and read-only output aliases are not extra host determinants.
- Values MUST match the executable schema’s types and declared domains, including enum identity
and legal enum values where applicable, without lossy coercion through a Store carrier. Point
metadata is not that schema: current
IoInventoryincludes internal points, projects enums toInt, and omits strings. It cannot by itself establish the required executable input set. - Type seeds, previously staged values, held Store samples, absent quality metadata and host substitutions MUST NOT become implicit defaults. Engine-level optionality is executable schema semantics, not permission for OCE to invent sensor quality, freshness or fallback policy. This contract adds no blanket finite-Real or plausibility rule for signal values; finite time is a separate requirement.
- Native preparation and transition MUST use only the supplied values and engine execution context, with no Store reads, writes or host callbacks that add hidden input determinants. A host or convenience adapter can obtain observations before submission; it owns their coherence and quality. “Complete” does not prove simultaneous sampling or physical validity.
Compatibility and generation roles
These are semantic roles, not proposed public field or type names:
| Role | Meaning and limit |
|---|---|
| Loaded-executable context | The engine-local successful load incarnation and the executable it installed. Successful reload invalidates prior prepared frames and resolved references, even when reloading identical bytes or reusing the same point names. |
| Executable IO compatibility | Canonical boundary identities, direction, types/domains, fan-out and explicit omission semantics used to interpret inputs and outputs. Equality of authored model names or point counts is insufficient. |
| Accepted-transition correlation | An unambiguous association among an accepted input, its one transition, and its completed output/diagnostics within the compatible run. Equal timestamps identify different transitions; refused submissions consume no accepted-transition position. |
| Replay compatibility and position | The canonical per-frame record carries exact public facts and placement; the host authenticates executable/build/prior-state context and ordered sequence position. Ephemeral load tokens and Engine-lifetime sequence are not serialized. |
A frame or resolved reference MUST be checked against the current loaded-executable and IO context before mutation; a stale context refuses rather than silently rebinding names. Preparation alone does not grant permission to commit after reload or after the time guard has advanced beyond the submitted time. Ordinary failed load retains the prior engine-local context under the existing in-memory replacement guarantee; external Store effects still have their separate compensation boundary. Compatibility-changing reconfiguration cannot silently reuse old preparation.
This fence is not a deployment generation, lease, authentication token, sensor freshness test, command authorization or restored actuator ownership. A host deployment identifier cannot replace the engine check, and a passing engine check cannot replace host admission. Authored/source model, exported-document and catalog identities retain their distinct roles. Current snapshot executable compatibility is not proof that a reference belongs to the current successful load incarnation.
Preparation supplies the concrete reference representation and typed refusal surface; native execution supplies accepted-result correlation. M03 owns typed identity/compatibility layers and canonical state/replay representation, including continuation/rewind correlation. Future representation choices cannot weaken these reload and correlation semantics. No snapshot fields, catalog identities or state revisions change here, and hosts are not asked to parse private snapshot bytes or build another replay format to fill this gap.
Prevalidation and refusal matrix
All ordinary refusal conditions MUST be validated before any execution/replay mutation. The whole candidate is resolved and checked before any input prefix is staged. There is no evaluation to discover an ordinary input error. Typed error names and deterministic precedence for preparation are specified below; execution retains this prevalidation boundary.
| Condition | Required outcome before mutation |
|---|---|
| No successfully loaded executable | Refuse; an empty engine is not an executable with zero inputs. |
| Pending parameter edits | Refuse until resume; halted alone does not prohibit execution. |
| Unknown or non-input identity | Refuse; do not ignore extra values or treat output aliases/internal driven points as boundary inputs. |
| Duplicate boundary input | Refuse even if both values are bit-identical; do not use first-wins or last-wins. Fan-out is not a duplicate. |
| Missing required input | Refuse; no hold-last, zero/false seed or Store fill-in. Apply only explicit executable omission semantics. |
| Wrong type or declared domain | Refuse the complete candidate, including any otherwise valid prefix. |
| NaN or either infinite model time | Refuse. |
| Finite time below the preceding accepted model time | Refuse. |
| Time outside loaded-block representability | Refuse under the existing model-time limits, not by executing first. |
| Stale loaded-executable or IO compatibility context | Refuse frames and references from a superseded successful load; no implicit rebinding. |
| Accepted-frame sequence exhausted | Refuse before staging; never wrap or reset the sequence. |
| All checks pass, including equal finite time | Accept exactly one HostTick v1 transition. |
For every ordinary refused frame, the entire observable engine execution/replay image MUST remain unchanged: model time and monotonic guard; state words; connector image (including staged inputs); visible outputs and output generation; completed-frame diagnostics; replay identity and accepted-transition position. Refusal also preserves existing mutation/readiness boundaries, rather than closing a fresh durable-restore window by staging a prefix. The refusal report describes the attempt; it does not replace the last completed outcome or masquerade as a completed diagnostic frame.
This guarantee excludes panic, process death, allocation failure, cancellation and concurrency outside current guarantees. It is not persistence, Store rollback, delivery acknowledgment or an actuator guarantee. Native Store noninterference does not retroactively undo earlier host/adapter operations. No scheduler, deadline guarantee or universal panic freedom is introduced.
Accepted transition and immutable outcome
One successful complete frame MUST perform exactly one HostTick v1 transition: stage the validated values, emit the frozen schedule once from entry state/current inputs, then update stateful blocks once and expose the completed boundary outputs. Model time is finite nondecreasing seconds. An equal finite timestamp is valid and advances again; it is neither a read nor an idempotent retry. There is no hidden event iteration, convergence test or repeated evaluation to reach a fixed point.
The accepted output frame MUST be immutable and bound to that accepted input/transition and its loaded-executable/IO compatibility context. It contains all completed executable boundary outputs with their identities and types, plus the execution diagnostics belonging to that transition. Later execution or reload cannot change a previously retained outcome. It is not merely an alias to a mutable latest-output view, a subset of convenient point rows, or a Store write receipt.
Output and diagnostic ordering and compatibility/sequence semantics MUST support deterministic host correlation, including two successes at the same time and an intervening refused attempt. Runtime assertion diagnostics follow the existing Warning-only law: warnings do not reject or roll back a transition, escalate to an Error level, or implement an interlock. Collection covers execution diagnostics, not load/export receipt history, host quality assessments or persistence/write errors. Diagnostic source semantics are preserved; no new instance-identity promise is inferred. Wall-clock latency measurements are not deterministic replay identity.
Producing or retaining an output frame means computation completed, not that an adapter persisted it or equipment received it. Execution performs no Store write and has no post-write error path. External delivery failure remains the host’s responsibility and does not relabel a completed frame. Equal-time resubmission is another transition, not a retry of external delivery.
Legacy paths and migration
Revision 9 removes tick/tick_with, set_input, simulate, step_realtime, realtime epoch
configuration, Outputs/Engine::outputs, and the simulation/source/trace/report types. There
are no aliases, deprecation bridges or profile tags. Sibling consumers migrate to complete frames.
Collect every required observation, call prepare_frame, then consume the plan with execute_frame.
Missing and duplicate inputs refuse; there is no sparse, last-wins, hold-last or Store-backed
fallback. A host simulation loop supplies each complete frame and owns trace capture. Repeated
loops continue the current engine; use a fresh load or explicit compatible checkpoint restore when
a restart or rewind is intended. A loop is not a whole-horizon transaction.
get_output and watch are explicitly latest-state, non-receipt inspection. They can read internal
points, and load/restore/resume may replace their state without a frame. Only CompletedFrame
retains committed boundary results and diagnostics. There is no engine Store-write helper.
The private transition_host_tick remains the sole infallible evaluation/refresh core after frame
preflight. The instrumented core test detects
bypass, double emit/update and entry on refusal. The conformance driver uses complete frames in both
uniform and event-aligned modes; equivalent complete schedules retain bit-exact traces. This does
not claim Modelica event iteration, whole-horizon rollback, durability or host qualification.
Host and downstream boundaries
Quality, freshness, plausibility, missing-data reaction, NO_EVAL, safe-state selection, scheduling,
wall-clock mapping, persistence, authentication/authorization, deployment fencing, equipment
interlocks and actuation remain host responsibilities. NO_EVAL means
not executing, not submitting a fabricated zero frame or calling halt as an equipment stop.
Library keeps artifact verification; Studio keeps authoring/translation and simulation integration; Edge keeps deployment, observation admission, NO_EVAL and command boundaries; Sim keeps closed-loop simulation and replay integration. Runtime is only an additive future M05-PR03 consumer/host qualification candidate. BOPTEST results would be Runtime host evidence, not OCE equivalence or reassignment of Sim. No downstream qualification is claimed here.
Tokio, Axum, SQL, HTTP/MCP, drivers, quality/staleness policy, leases, commands and fallback services stay outside OCE. Consumers use the engine-owned evaluator, snapshot and canonical per-frame replay contracts; this work does not create a second evaluator, snapshot or replay stack.
Evidence and remaining acceptance work
The frame refusal controls replace historical gap tests: omission, duplicates and wrong types preserve the prior image without staging a prefix. The Store noninterference suite proves that even supplied Store samples cannot fill missing determinants or overwrite complete values. The Pre profile tests retain the fixed HostTick recurrence and snapshot continuation evidence. Compiler absence controls cover all supported feature selections; they do not qualify downstream consumers. The product checker is still a bounded traceability/claim-sentinel tool, not proof of semantic compliance.
Preparation evidence below fulfills PC-032; the execution evidence below fulfills PC-033 with equal-time correlation, retained immutable results and unchanged images on ordinary refusal. Explicit omission semantics would require a future executable schema change; none is invented here. The existing HostTick conformance limits and later cross-platform/replay qualification still apply.
Current preparation API
Engine::input_definitions() returns an owned Vec<InputDefinition> in lexical UTF-8 canonical
path order. Each row has path, exact native value_type, and inclusive min/max as optional
native Values. This is the represented executable boundary (external_inputs), not the point
inventory or discarded source declarations with no executable consumer. Every row is required;
the current schema has no optionality/default declaration. Fan-out yields one row, with the
intersection of the boundary declaration’s and all targets’ bounds. Empty intersections accept no
value. Real zero-bound ties have deterministic bits; a NaN bound never disappears in intersection.
Integer bounds remain exact i64 values; absent Integer bounds use the documented i32 defaults,
while explicit bounds are retained. Enum bounds carry the class and legal ordinal endpoints.
No Store carrier, handle or public connector index is involved.
Keys are the expanded identities retained by ingest. The current resolver admits no input aliases: compact names are expanded at ingest, not at submission, and elided child names are not alternate setters. Output aliases are read-only. An injected-resolver-alias control checks that two spellings mapped to one logical input cannot defeat duplicate detection. It does not establish a public alias namespace. String/enum schema handling is total, but no current registry block offers those signal ports; private detached probes do not claim broader CXF support.
Engine::prepare_frame(time, &[(&str, Value)]) returns PreparedInputFrame. The plan owns exact
values and every resolved target; keys are borrowed only during the call. It is opaque and has no
serialization or public constructor. Engine::execute_frame consumes it. Editing a copied definition
does not alter validation. Neither preparation success nor preparation refusal stages a value,
evaluates, calls Store, replaces diagnostics,
changes watches/outputs/time, or closes durable-restore readiness. There is no new replay image.
First-cause refusal precedence is:
State(NoLoadedModel), thenState(PendingParameterEdits).NonFiniteTime,TimeRegression, thenModelTimeUnrepresentable, in that order.- The lexically first invalid submitted key:
FrameUnknownInputorFrameNotInput(outputs and internally driven input points). These errors keep at most 64 UTF-8 bytes, cut at a character boundary, and the original key byte count. They do not clone an arbitrary-size submitted key. FrameDuplicateInput, thenFrameMissingInput, each naming the lowest canonical input path.- The first canonical input with a bad value:
InputTypebeforeInputDomainfor that input.
This precedence is independent of entry order. Refusals do not accumulate a report. Unbounded Real values retain NaN payloads/infinities; a declared comparison bound has to hold, so any bound rejects NaN and finite bounds reject the respective infinity. No blanket finite-signal or equipment policy is introduced. Finite equal time is valid.
The internal check_prepared_frame seam checks readiness, incarnation, and current time eligibility
before execution can use resolved targets. It returns StalePreparedFrame for a
different engine or superseded load/rebuild, without rebinding names. Successful identical-byte
reload also invalidates. Failed load preserves the previous incarnation. Dirty resume fences before
effective model mutation, even for same-value edits; clean resume and compatible checkpoint/durable
restore alone retain the incarnation. Advancing the clock can still make a retained plan’s time
ineligible. The fence is a retained process-local allocation identity, not a serialized counter,
model name, Store handle, deployment generation or authentication token.
Preparation costs and evidence limits
For N logical inputs and T total fan-out targets, successful preparation allocates N+2 buffers for nonempty N: one temporary N-slot reference array, one N-entry plan, and one target array per input. Values clone without copying String bytes. Retained capacity is N plan entries plus T targets; zero-input preparation allocates no buffers. Input lookup scans submitted key bytes; value checks and target copies are proportional to N+T. The load-time schema sorts canonical keys once. The cooling-only-controller census repeats preparation 128 times, checks exact allocation/byte formulas and capacities, requires no retained allocation after drop, and has a counter positive control. This is a synchronous allocation census, not a latency, throughput or general peak-memory claim. Definition snapshots separately allocate owned metadata.
The public refusal matrix compares fresh and advanced stateful snapshots, output/watch values, restore readiness and Store call counts. The public ordering tests and private plan/lifecycle/domain/census tests pin independently authored expected values and checked-in bit/diagnostic goldens. The nonserialization compile-fail example and exact facade baseline cover opacity. Removing duplicate detection, dropping a fan-out tail, coercing integers via f64, changing signed-zero bits or omitting invalidation is detected by the corresponding assertions. No external Modelica oracle exists for this OCE-specific policy.
Current execution API
Engine::execute_frame(PreparedInputFrame) -> Result<CompletedFrame, OcError> consumes one plan
by value, even on refusal. The plan cannot be cloned, serialized or submitted twice. Preflight
precedence is unloaded, pending edits, stale incarnation, nonfinite time, regression, block-time
representability, then FrameSequenceExhausted. Preparation has already resolved and validated
every input and fan-out target. Execution rechecks the mutable conditions, without name rebinding.
After preflight, the implementation stages the entire plan, closes durable restore, calls the
existing infallible evaluator once with the Warning collector, updates prev_t and latest-state inspection
once, increments the accepted-frame sequence and captures the result. All ordinary returned errors
are before this boundary. No Store operation, host callback, shadow RunState, undo log or rollback
is involved. Allocation failure remains excluded, including during result capture.
CompletedFrame is owned, Clone + Debug + Send + Sync, with private fields and read-only
time(), sequence(), inputs(), outputs() and diagnostics() accessors. Inputs and outputs are (String, Value)
pairs; cloning a result gives an independent owned result. Real bits are copied, not normalized by
capture (individual block arithmetic retains its existing numerical policy). No public connector
indices, Store handles, mutable latest-view, schedule or serialized identity are returned.
The output set is the same disjoint union as Topology.boundary_outputs: represented elided
root declarations plus lowered pass-through outputs. It is sorted by canonical lexical UTF-8
identity, not source order. Distinct declarations sharing a driver stay distinct; internal driver
paths and the pass-through listing are not appended as duplicate aliases. Undriven source-only
declarations are absent under the existing ingest warning contract. Zero boundary outputs is valid,
including an Assert-only boundary with internal outputs. Inventory and host-selected trace columns are
not the authority for this set.
Sequence starts at one and advances only on a successful native complete-frame commit. It never
decreases or resets in one Engine lifetime, including successful reload, dirty/clean resume,
checkpoint rewind and durable restore. Refusals consume no position. Thus equal-time commits remain
distinct. The private retained Arc<()>
incarnation binds each result to its loaded executable/IO and fixed build/profile context without
exposing pointer identity. Sequence is correlation only: not replay position, snapshot generation,
durability, cross-process identity, deployment authority, lease, authentication or freshness.
Neither it nor the result is serialized into existing state/checkpoint bytes. No last-result cache
is added to Engine; the caller owns retained outcomes, which remain unchanged by later operations.
The receipt also retains every accepted canonical boundary input, moving the exact prepared value
and copying the canonical schema path. replay_record() captures a separate bounded canonical
record, not a second transition; oversized record capture can refuse after successful execution.
Its v1 contract leaves authenticated order and snapshot sidecars with the host.
Execution evidence and cost boundary
- Public success goldens: hand-derived Add, sampled delay and equal-time Pre feedback; fan-out, native pass-through bits, lexical/many-to-one boundaries and ordered Warning diagnostics. These OCE-specific scenarios are not a Modelica event-iteration oracle.
- Public preservation matrix and private preflight controls: fresh/advanced connector/word/time/output/scratch images, snapshot and checkpoint bytes, watches, restore readiness, sequence and Store counts. Private injection exercises nonfinite/unrepresentable time and sequence exhaustion; opaque public plans cannot be forged. The instrumented block boundary detects even idempotent double updates. No postcommit ordinary failure is invented.
- G36 controller: two complete 1,441-row, ten-boundary-output runs against the existing independent Tier-A HostTick reference, not engine self-output or unrestricted Modelica equivalence.
- Allocation and latency harness: exact counters and positive control, 128 repetitions each of five fixtures. For B nonempty boundary outputs, output capture allocates B path buffers plus one pair vector; empty B allocates none. Accepted-input retention adds N path buffers plus one N-pair vector for nonempty N, and moves prepared Values. Placement capture scans the loaded class set with the state portability predicate. Warnings add their owned source/message buffers and a geometrically growing event vector. Staging clones only the prepared fan-out values; preparation’s separate N/T formula remains above. Existing evaluator allocation exceptions remain, and latest-state inspection refreshes after each commit. No whole-engine/state copy is charged as result capture. See measured observations for debug/release timing scope; no universal speed or latency-ratio guarantee follows.
Compile-fail rustdoc pins nonserialization and single-use preparation. Exact public baselines and shape guards cover the frame-only API. Hosted architecture qualification and actual downstream host adoption remain separate. PC-035 promotes this contraction only, not stable release, persistence or equipment claims.