Skip to content

Portable public API specification

Since: 0.1.0

This document defines the normative language-neutral public API shared by the Swift, Kotlin, Go, Rust, C++, C#, and Unity implementations. It specifies concepts and observable behavior, not the spelling imposed by one language.

The C# implementation is the current reference implementation. A port may use idiomatic constructors, optionals, result types, collection views, naming, and callback forms, but it must preserve the responsibilities, state transitions, ordering, errors, and data described here. The public C# API snapshot is a regression tool for the C# binding and is not the portable API definition.

The optional standalone presentation API is specified in Syntax highlighting. Its C# and Swift packages have no Runtime, Compiler, Bridge or Conformance dependency and are not part of VM execution.

Scope and dependency direction

The portable product modules and independent verification tooling are separated as follows:

GameEventScript Runtime
values, messages, program, binary codec, validation, host, VM, literal parsing
GameEventScript Compiler
source builder, lexer, parser, validation, optimizer, bytecode generation
-> depends only on GameEventScript Runtime
Independent Conformance runner (Python, repository tooling)
Markdown parser, normalized test model, assertions, reports, received output
-> communicates with internal implementation adapters over JSON
-> C# and Swift adapters depend on their Runtime and Compiler; no public Conformance library

Runtime must load and execute precompiled Programs without a source compiler. Shared value semantics, text/number conversions, runtime literal parsing, Program validation, and declarative external-type definitions remain in Runtime. There is no additional shared Core module. Compiler uses these Runtime contracts; Runtime never references Compiler or Conformance. Compiler never references Conformance or language-specific adapters. Both portable modules remain synchronous and independent of filesystem, network, threads, and test frameworks.

Conformance is repository tooling, not a published NuGet library or root SwiftPM product. The Python runner owns assertions; the internal C# and Swift adapters expose only the process protocol in PythonRunner.md. Neither implementation publishes a Conformance library.

The following documents own detailed behavior and are incorporated by reference:

C# reflection attributes, delegates, locks, threads, tasks, System.Type, and dictionary conveniences belong to CSharpBridge. They are public C# adapters, not portable Runtime or Compiler concepts. Other ports may provide equivalent conveniences, but conformance code and portable application code must not require them.

Embedding API baseline and compatibility

The reviewed application API baseline consists of Runtime, the optional Compiler and the native Bridge in each implementation. SyntaxHighlighter is a separate optional product with its own contract. CLI and Conformance are not application library products. Public implementation details must not be inferred from private types, compiler SPI or the shape of generated code.

From 1.0 onward, compatible releases preserve the supported public source API: existing calls and implementations of public callback interfaces/protocols must remain valid under the declared platform/toolchain requirements. Signatures, argument labels/order, specified defaults, result interpretation, ownership, lifetime and error contracts are part of that surface. An addition is compatible only if existing conforming consumers continue to work; for example, requiring an extra callback implementation is not automatically compatible merely because it adds a member. Deprecation may guide migration but does not permit removal within a compatible major line.

Native naming, value/reference representation and error transport may differ between languages as specified below; each published language binding retains its own source API compatibility. There is no requirement for identical symbol names across languages. Native object layout and compiler-generated ABI are not the portable binary interchange contract; .gesb compatibility is owned by Binary format.

The existing compiler and host builder workflows are the selected baseline; no additional manager/facade or combined load-and-run operation is required. Programs remain reusable data. Host-specific bindings, execution and lifecycle handles remain separate from compilation. Existing native adapters provide callbacks and external type bindings without bringing Compiler into Runtime or Bridge consumers.

The approved C# and Swift snapshots record each implementation’s declared surface and are checked against freshly built APIs. XML documentation and authored Swift symbol documentation are checked alongside them. Snapshots detect signature drift; the normative contracts and shared Conformance govern behavior. Keeping a snapshot unchanged does not justify changing ownership, defaults or execution semantics. The review evidence and intentional language differences are summarized in the API review.

This baseline does not turn a 0.x release into a stable 1.0 release. The version policy governs any subsequent pre-1.0 correction; required bytecode changes must be rechecked for impact on the public Program representation and codec APIs before 1.0.

Normative conventions

Names used by this specification

Type and operation names below are conceptual PascalCase names. They correspond closely to the C# reference API only to make cross-checking practical. Swift, Kotlin, Go, Rust, and C++ may use their normal naming and callable conventions. Removing a concept, changing its ownership, or changing its result is not an idiomatic mapping and is therefore not permitted.

List<T> means an ordered immutable view unless the operation is explicitly a builder input. Bytes means an ordered immutable byte sequence. Optional<T> means explicit absence, not a sentinel native object. Fixed-width integer names refer to their exact bit width.

Nullability and invalid arguments

  • Required object, string, callback, and sequence inputs must be present.
  • Optional inputs are identified explicitly. An absent optional sequence has the same meaning as an empty sequence only where this document says so.
  • Public output collections are never null. They are empty when no items exist.
  • Collection elements that are not explicitly optional must not be null.
  • Invalid caller arguments fail synchronously before observable state changes. Each language uses its idiomatic argument/precondition error.
  • Script, decode, link, and runtime failures use structured GES diagnostics or format errors. Platform exception text and exception class names are never a portable comparison value.
  • Portable identifiers use the grammar and normalization rules in Text. Comparisons are ordinal and culture independent.

Copy and reference semantics

  • Immutable data objects copy mutable input sequences before returning from construction. Their public sequences are read-only views.
  • Value records may be native value types or immutable reference objects. Their equality and hash behavior must follow portable content, never object identity.
  • Program, messages, signatures, definitions, diagnostics, and normalized conformance models may be shared for concurrent read access.
  • Builder, Host, Context, lifecycle handles, random generator, extension calls, and runner callbacks are identity-bearing reference objects.
  • Returning a read-only view must not expose storage that a caller can mutate.

Synchronization and reentrancy

  • Core operations are synchronous, threadless, and internally unsynchronized.
  • Immutable values and programs permit concurrent reads. A builder, host, context, random generator, or active call object does not permit concurrent use.
  • A host is serial but not thread-affine. A later call may run on another thread after an embedding-provided happens-before handoff.
  • Host pumping is not reentrant: a callback must not call ExecuteFrame or RunToCompletion on the active Host. Synchronous Receive, Load, Subscribe, Detach, and Unsubscribe calls from a native callback are permitted and obey the enqueue-time snapshot rules; they must return before the callback returns.
  • Observer and publish-sink callbacks run synchronously on the pumping caller.
  • Portable library operations remain synchronous. Conformance process orchestration belongs to repository tooling, outside the application library contract.

Allocation contract

The allocation contract applies to the warmed runtime hot path, not compilation, Markdown/YAML parsing, .gesb decoding, message construction, debug dumping, or report creation.

After host warmup, queue dispatch, handler selection, execution-result creation, publish-result creation, VM stepping/resume, and random scope operations must not allocate heap objects by themselves. Value-style execution and publish results must therefore remain representable without heap allocation. User callbacks, extension implementations, emitted messages, and values may allocate.

The dispatch guarantee applies equally to exact-signature and message-name handlers, with no observer or with observer callbacks that allocate nothing. Obtaining the dispatch signature for VM entry or observer callbacks is part of the runtime’s own dispatch work and must not allocate after warmup.

Compiler API

CompilerBuilder

CompilerBuilder is a mutable, single-caller configuration and source accumulator. It owns snapshots of all inputs needed after an operation returns.

Operation Contract
Create() Returns an empty builder with debug information set to All, program version 0, and no external-type catalog.
AddScript(text, sourceName?) Adds one logical source in call order. Text is required and must be valid Unicode. The optional source name is diagnostic/debug identity, not a path to open; absence uses the implementation’s stable unknown-source name. The builder retains its own source snapshot.
WithDebugInfo(options) Replaces the debug emission mask. None, DebugSymbols, SourceMap, and SourceArchive are independent flags; All is their union.
WithProgramVersion(version) Sets the exact unsigned 64-bit application program version; zero means development/unspecified.
WithExternalTypeCatalog(catalog) Sets the immutable declarative catalog used for compile-time resolution. The catalog is required when configured and contains no runtime callbacks.
Compile(options) Parses all added sources as one compilation, validates, optimizes, materializes an immutable Program, and validates that Program before returning it. Sources retain their AddScript order and source IDs.

CompileOptions is an immutable or snapshot-at-call value containing DebugInfo and ProgramVersion. Operation-local options override matching builder defaults. Ports may choose either builder setters or one options object, provided there is one unambiguous resolved configuration. Compilation never modifies an earlier Program.

An empty builder produces the valid empty Program named EmptyModule. Malformed source fails with one or more structured diagnostics. The fixed source nesting limits in Language apply to every compilation. Exceeding them produces the Parse diagnostic parse.sourceNestingExceeded (DiagnosticCodes.ParseSourceNestingExceeded in C#), including source identity and location. CompileOptions cannot disable these limits. CompileException is the C# transport carrying an ordered, non-empty diagnostic list. A port may return a result union instead. Diagnostics remain the portable failure value.

The C# compiler entry point is GameEventScriptBuilder.Create().AddScript(text, sourceName).Compile(options). Host construction independently uses GameEventScriptHost.CreateBuilder().

Program and bytecode data API

Program

Program is the immutable, reusable, language-neutral parsed representation of one .gesb artifact. Only Compiler and ProgramReader create Programs publicly. There is no free-form public Program constructor.

The Program exposes:

Property Contract
FormatVersion .gesb format version represented by the model.
ModuleName, ProgramVersion Stable program identity and unsigned application version from ProgramMetadata.
RequiredRegisterCount, RequiredCallStackDepth Maxima across executable handlers, verified by the validator.
Metadata, StringConstants, UInt16IndexLists, Bindings, Code Exactly one required immutable runtime segment of each kind.
DebugSymbols, SourceMap, SourceArchive, BuildMetadata Independent optional known segments. Absence is represented explicitly.
OpaqueSections Immutable retained unknown optional sections in original relative order.

All segment entry order, duplicate string constants, instruction bits, source bytes, and opaque payload bytes are meaningful immutable data. Resolving a string or index list returns a decoded value/view without mutating Program. Out-of-range access fails as an argument/index error for direct low-level API use; Reader, Writer, Validator, and Host must report structured format/link failures before such access can occur for untrusted input.

Required segment views

The portable segment model contains these data records and operations:

  • ProgramMetadataSegment: module name, required register count, required call depth, and program version.
  • StringConstantSegment: immutable UTF-8 storage and slice records; Resolve(index) strictly decodes one string and ResolveSize(index) returns its byte length.
  • UInt16IndexListSegment: immutable u16 storage and slice records; Resolve(index) returns one immutable indexed list view.
  • BindingSegment: ordered bindings containing kind, ID, name index, ordered argument-name indices, required/excluded tag indices, optional entry address, and per-handler resource requirements.
  • CodeSegment: ordered 16-byte logical instructions; Count/Length and indexed access do not expose mutable program storage.

ReadOnlyArray<T>, string slices, and UInt16IndexList are representation helpers for immutable ordered access. A port may use its native immutable list or slice type instead. Count and Length are aliases where both exist in C#; they must report the same value.

An index into a resolved UInt16IndexList is zero-based relative to that list. Only 0 <= index < Length is valid, irrespective of storage before or after the slice. An empty list has no valid index. Access outside these relative bounds fails as an argument/index error before applying the storage offset.

Instruction and numeric identifier types

BytecodeInstruction exposes opcode, unit/flag byte, three u16 words, and a u64 payload with the semantic aliases specified by Bytecode. The aliases read and write the same logical words/bits. C# struct layout and native endianness do not define the binary format.

The following enum-like types expose the fixed numeric IDs from the bytecode and binary specifications and must reject undefined values at validation boundaries:

  • BytecodeOpCode, BytecodeTypeKind, BytecodeInstructionUnit, InstructionFlag, BytecodePatternKind, and BytecodeSeriesKind;
  • BinaryBindKind, SectionType, SectionFlags, and DebugSymbolKind.

BytecodeOpCode.ParseLiteral exposes the portable parse operation at opcode 0xD9, with operand and limit semantics owned by Bytecode.

BytecodeInstructionUnits supplies pure conversions between type names, units, and suffixes. Parse operations return absence for an unknown valid string; predicate operations never use locale; formatting returns canonical GES text. BinaryFormat exposes the fixed V1 magic bytes, format version, file-header size, and section-header size as named constants; their numeric values are owned by BinaryFormat.md.

Optional program data

  • DebugSymbolsSegment exposes ordered DebugSymbol records. Each record has kind, physical register ID, name, and half-open code range.
  • SourceMapSegment exposes ordered SourceMapSource and SourceMapEntry records. Hashes and line offsets are immutable bytes/integers; source and code ranges are half-open and use the units defined by the binary specification.
  • SourceArchiveSegment exposes ordered SourceArchiveEntry records. ResolveText() strictly decodes retained UTF-8 content.
  • BuildMetadataSegment exposes required non-empty compiler ID and compiler version when the optional segment exists. Runtime decisions must ignore them.
  • OpaqueSection exposes type, flags, version, original ordinal, and immutable raw payload. Only unknown optional sections may be represented this way.

The public constructors of individual segment records are data helpers. They defensively copy their inputs but cannot assemble a Program and do not bypass Program validation.

Program codec, validation, and dump API

ProgramReader

ProgramReader.Read(bytes, options) -> Program consumes caller-provided bytes only. It performs overflow-safe bounded structural decoding followed by complete semantic validation. It retains no caller buffer. Success returns a fully valid immutable Program; failure returns/throws ProgramFormatError with a stable ProgramFormatErrorCode and optional byte offset, section type, and entry index.

ProgramReadOptions contains a required ProgramReadLimits snapshot and one retention mode:

  • PreserveAll: parse all supported known sections and retain unknown optional sections as opaque data;
  • PreserveKnown: parse supported known sections and discard unknown optional sections;
  • RuntimeOnly: materialize required runtime sections only.

ProgramReadLimits exposes maxima for file bytes, section count, source archive bytes, retained opaque bytes, instructions, bindings, strings, and index lists. All values are validated before allocation. The shared Default object is read-only by convention; a mutable language mapping must snapshot settings at the start of Read.

ProgramWriter

Operation Contract
GetEncodedSize(program) Fully validates Program and returns the exact canonical byte count with checked arithmetic.
Write(program, destination) Fully validates before modifying destination, requires sufficient caller storage, writes canonical Little Endian .gesb, and returns bytes written.
ToArray(program) Allocates exactly the required output byte array and delegates to canonical Write semantics.

The writer reconstructs every known section from structured data. Only opaque payloads are copied raw. It performs no file or stream I/O.

ProgramValidator and ProgramDumper

ProgramValidator.Validate(program) is deterministic, side-effect free, and performs the same complete semantic checks used by Reader, Writer, and Host.Load. It does not link executable callbacks. Failure uses ProgramFormatError.

ProgramDumper.Dump(program, includeInstructionAddresses = false) returns canonical .gesa text for a valid Program. It uses embedded debug/source segments when available, performs no file I/O, and never changes Program. The dumper is not a trust boundary and does not replace explicit Program validation. Dump allocation is outside the runtime hot path.

Message and value API

MessageSignature

MessageSignature is immutable content identified by normalized message name plus ordered parameter labels. Tags and values are not part of a signature.

Operation Contract
Create(name, parameters) Normalizes and copies the name and ordered labels. An absent parameter sequence means empty. Repeated labels remain part of the signature; constructing actual MessageArguments later rejects duplicate named labels while repeated _ labels remain permitted.
NormalizeMessageName(name) Trims portable ASCII edge whitespace and validates the message-name grammar; absent input yields empty only for this normalization helper.
NormalizeParameterName(name) Trims portable ASCII edge whitespace; absent/empty becomes _; otherwise validates a portable identifier.
CreateSignatureId(name, parameters) Returns canonical Name(label,...) from normalized content.
Matches(message) True exactly when normalized name and complete signature ID are equal ordinally. Message tags and values are ignored.
CreateMessage(values) Binds values in parameter order. Returns absence when arity does not match or the signature is Empty.
WithArguments(values...) Same binding, but invalid arity is a synchronous argument error.

Signature equality and hashing use canonical signature content. Empty is a read-only sentinel and cannot create a message. Public parameter views cannot change the signature or any message created from it. Message factories snapshot caller-owned value collections; later changes to those collections cannot change an existing message.

Message and arguments

MessageArgument is an immutable name/value pair. MessageArguments is an immutable ordered collection exposing Count, indexed argument access, indexed name/value access, and lookup by normalized named label. Lookup returns absence when not found. It never converts the collection to unordered semantics.

Message.Create(name, arguments?, tags?) validates and copies the logical inputs. Absent arguments/tags mean empty. WithTags(tags) returns a new Message whose normalized tag set is the stable merge of existing and supplied tags; it does not mutate the original. HasTag(tag) uses normalized ordinal matching. Public tag and argument-label views cannot modify a message, its signature, or any other message sharing its immutable data. This also applies after a message has been enqueued: its delivery tags and argument labels remain unchanged.

Message equality compares signature, argument values in order, and normalized tags in order. Hashing must preserve the equal-values-have-equal-hash invariant. Human formatting is diagnostic only and must not be parsed as transport.

Value

Value is the compact tagged GES runtime value. A language may map it to a struct or another compact representation. Public factory operations create:

  • Nothing and Boolean;
  • signed-64 Integer, Binary64 Float, Percentage, and numeric values with a unit;
  • Text and Tag;
  • Vector and Point;
  • IntegerRange and FloatRange;
  • Dice from ordered roll values;
  • immutable List and Map;
  • Handler signature and Message;
  • immutable Record by type name and ordered fields;
  • External values through the external-type boundary.

Factory operations snapshot mutable arrays/maps before returning. The portable ordered map/record model must not derive order from platform dictionary iteration. C# dictionary factories belong to CSharpBridge and delegate to the portable ordered factories; their canonical ordering is governed by Determinism.

ValueKind, ValueUnit, HasValue, IsNothing, IsNumeric, HasUnit, Length, CustomTypeName, coordinate/range/message/handler views, and typed read operations expose the active value. A typed As... operation on an incompatible kind follows the public value conversion contract and must never expose uninitialized native storage. AsList and AsMap return immutable views; AsDice returns an independent ordered copy in the C# mapping.

Numeric register storage is a numeric projection only. Boolean stores 0/1 and Dice stores its exact Int64 roll sum (zero for empty Dice). Nonnumeric values have no numeric projection; their numeric storage is cleared. Collection counts and cached Unicode scalar/range lengths belong to the immutable payload. GesValue.Length returns zero for scalar values and saturates collection lengths at Int32.MaxValue.

Value equality is the strict structural equality in Determinism, including numeric rules in Numbers. Hashing must be consistent with this equality. ToString is nonnormative diagnostic formatting.

ValueSlice, ValueArguments, and ValueMap are read-only runtime views. They expose Length, indexed values, kind/unit checks, typed reads, and map key lookup. Indexes are zero-based at the API boundary. Invalid indexes fail as argument/index errors; VM opcodes must instead evaluate according to Language semantics. A borrowed read-only value reference must not outlive the callback or operation that supplied its backing storage.

IntegerRange and FloatRange are immutable triples (from, to, step); they do not enumerate by themselves. Range termination and overflow behavior belong to the deterministic language semantics.

Host construction and lifecycle API

HostBuilder

HostBuilder is mutable and single-caller. Every Build() creates an independent Host and private random generator. Reusing a builder must not cause hosts to share queues, random state, runtime budgets, dispatch state, or VM state. Immutable configuration and configured registry, sink, or observer callback identities may be shared; the embedding then owns any synchronization and reentrancy requirements of those callbacks.

Operation Contract
WithRuntimeLimits(limits) Replaces the required immutable limits configuration. A language with mutable option objects must snapshot all scalar values at Build.
WithRandomSeed(seed) Selects the complete signed-64 deterministic seed and clears a prior entropy or sequence configuration.
WithRandomEntropy(entropy) Folds a non-empty entropy byte span into the deterministic seed with the portable mixing function and clears a prior direct seed or sequence configuration.
WithRandomSequence(values, fallbackSeed?) Copies a finite Binary64 start sequence, optionally selects a deterministic fallback seed, and clears a prior direct seed. Exhaustion continues with the private fallback generator.
WithRegistry(registry) Selects the extension registry. Absence is represented by an empty registry, not null.
WithExternalTypeRegistry(registry) Selects the runtime external-constructor registry.
WithPublishSink(sink) Selects the single outbound sink. Not calling it means no sink.
WithProfiler(profiler) Selects optional synchronous opcode instrumentation; omission disables it. See opcode instrumentation.
WithRuntimeObserver(observer) Selects the single observer. Not calling it means no observer.
WithClock(clock) Selects the borrowed monotonic clock; omission uses the system clock. See delayed-send scheduling.
Build() Validates configuration and returns a new loading, native-capable Host with no loaded Program or subscription.

RuntimeLimits

RuntimeLimits is a configuration snapshot with the following signed integer settings: processed events per RunToCompletion call, queued logical messages, execution steps per handler, register values, loop iterations, call depth, random scope depth, range items, generated collection items, dice count, and dice sides. The exact defaults are part of the public versioned API.

Setting Default
MaxProcessedEventsPerRun 64
MaxQueuedMessagesPerRun 0 (unlimited)
MaxExecutionSteps 100,000
MaxRegisterValues 512
MaxLoopIterations 100,000
MaxCallDepth 64
MaxRandomScopeDepth 16
MaxRangeItems 10,000
MaxGeneratedCollectionItems 10,000
MaxDiceCount 1,000
MaxDiceSides 1,000,000

A nonpositive value disables the processed-event, queue, execution-step, loop, range, generated-item, dice-count, or dice-side limit. A nonpositive register limit selects the portable default capacity of 512. Call depth is clamped to the encoded u16 range for linking; zero permits only root handlers. Random scope depth alone must be in 0...65535 and is validated at Build. Host.Load checks static register/call requirements before registration. Dynamic limit exhaustion is a structured runtime-limit result/observation, not a thrown script exception. The counting rules and boundary behavior for loops and incrementally generated collections are defined in Host runtime.

Host

Host owns one serial execution domain. The complete state machine and snapshot rules are in Host runtime.

Operation Contract
CreateBuilder() Returns a new HostBuilder.
Load(program, priority = 0) Validates, links and atomically registers one instance with an initialization snapshot when present. During initial loading it joins the Start group; on a ready host it joins the ordinary FIFO.
Start() Synchronously initializes the initial group without processing ordinary messages and returns StartResult. Repeated calls return the original result.
IsReady True after successful initial Start; later instance failures and queued messages do not reset it.
Subscribe(signature, handler, requiredTags?, excludedTags?, priority = 0) Adds one exact-signature native subscription and returns a Subscription. Inputs are copied/retained as immutable registration data.
SubscribeMessageName(name, handler, requiredTags?, excludedTags?, priority = 0) Adds a native subscription matching every signature of that normalized message name.
Receive(message) Captures current matching subscriptions and attempts to enqueue the logical message locally. Returns whether accepted. Before Start succeeds it returns false. It never pumps and never publishes outbound.
ExecuteFrame(opcodeBudget) Synchronously pumps on the caller until the scheduler budget pauses script execution, the Host becomes idle, or a runtime limit/error ends the call. Budget must be positive and the host must be ready.
RunToCompletion() Synchronously pumps until idle, only future messages remain, or a runtime limit/error terminates this pump call. The host must be ready. It creates no worker thread.
IsIdle True only when no active message/handler and no queued logical message exists.
IdleDurationMicroseconds Optional Int64 idle duration on a ready Host; see idle observation.
PendingMessageCount Number of queued logical messages according to HostRuntime; it never counts handler invocations.

Equal priority uses stable registration order. A logical message completes all captured handlers before the next logical message. Load/Subscribe/Detach/ Unsubscribe changes affect snapshots captured afterward only. Load rejects insufficient initialization-queue capacity with link.initializationQueueFull. The full atomicity and retry contract is defined in Host runtime.

Instance is a Host-owned lifecycle handle for one linked Program. Program is the original immutable object. Detach() returns true exactly once when it transitions attached to detached; later calls return false. IsAttached reports that transition. Detach never cancels handlers already captured by an enqueued message and never attaches to another Host.

Subscription is the equivalent native-handler lifecycle handle. Unsubscribe() returns true exactly once; later calls return false. IsSubscribed reports that transition. Both handle types retain host identity and a stable non-reused host-local registration ID, not a closure.

StartResult is an immutable value containing State (Ready, RuntimeError or RuntimeLimitReached) and optional structured Diagnostic. Ready does not imply an empty message queue. Instance.StartResult is absent while initialization is pending and retains its completed outcome after detachment or init failure. Init failure cancels captured recipients as specified by HostRuntime; ordinary Detach retains its existing captured-delivery semantics.

StartResult includes ExecutedOpcodes, ProcessedMessages, EmittedMessages, and PublishedMessages for initialization work, using the same counter meanings as ExecutionResult. A host result covers its initial group; an instance result covers that instance. Repeated Start returns the original counters.

Context, messaging, and execution results

Context

Exactly one Context exists per Host. The Host passes the same identity to native handlers and extensions. Context exposes Random, RuntimeLimits, the extension registry, IsIdle, and PendingMessageCount as borrowed Host views.

Emit(message) attempts local enqueue and returns acceptance. Convenience overloads construct a message from name and ordered arguments. Publish(message) first attempts the identical local enqueue, then invokes the configured outbound sink once unless a random fault gate forbids delivery or initialization defers it. It returns PublishResult.

Context has no independent queue, VM, or scheduler. It must not be used concurrently or retained for asynchronous calls after its callback. Emit and Publish do not recursively dispatch the new message.

PublishSink and PublishResult

PublishResult.OutboundDeferred distinguishes initialization publication waiting for commitment from a sink call already attempted. Its complete ordering and failure semantics are owned by HostRuntime.

PublishSink.Publish(message) -> bool is a synchronous, preferably nonblocking handoff. true means accepted by the next layer, not remotely delivered.

PublishResult is a value with:

  • LocalAccepted: local queue accepted the message;
  • OutboundAttempted: a configured sink was invoked;
  • OutboundAccepted: that invocation returned true;
  • OutboundDeferred: outbound delivery is waiting for successful initialization;
  • AnyAccepted: derived LocalAccepted || OutboundAccepted || OutboundDeferred.

No sink produces attempted=false/accepted=false. Sink rejection is not an error. A sink exception becomes a runtime diagnostic and outbound rejection; it never rolls back successful local enqueue and does not escape the portable Host API.

NativeMessageHandler and RuntimeObserver

NativeMessageHandler.Handle(message, context) is one atomic trusted callback. It may Emit, Publish, and use Context.Random synchronously. An implementation failure becomes runtime.nativeHandlerFailure; it aborts that handler, restores runtime boundaries, and does not corrupt Host state.

RuntimeObserver receives synchronous callbacks in exact dispatch order:

  • MessageEmitted(message, accepted) after each Emit attempt;
  • MessagePublished(message, result) after local/outbound Publish processing;
  • DispatchStarted(message, dispatchSignatureId) immediately before a handler;
  • DispatchCompleted(message, dispatchSignatureId) when the handler boundary closes, including cleanup after a handler failure or limit;
  • RuntimeLimitReached(name, detail, limit) for a limit stop;
  • RuntimeError(diagnostic) for a runtime diagnostic.

An observer is observational only and has no return value. Observer callbacks must not throw. Throwing is an embedding-contract violation, is not converted to a GES diagnostic, and lies outside Host state/recovery guarantees. This differs deliberately from publish-sink exceptions, which the Host contains as specified above.

Opcode instrumentation

Since: Unreleased

WithProfiler / withProfiler selects an optional synchronous IGameEventScriptProfiler / GameEventScriptProfiler factory. Swift also accepts profiler in its Host initializer. The Host calls CreateProgramProfiler(program) / createProgramProfiler(program) once per linked load instance, before execution. The result implements IGameEventScriptProgramProfiler / GameEventScriptProgramProfiler and remains host-specific; no instrumentation state enters the immutable Program. Repeated loads, including identical Programs or module names, receive separate collectors. The factory can retain collectors for reporting after detach.

The VM announces sequential loop phases through PhaseStarting(phase) / phaseStarting(phase): StateCheck, BudgetCheck, SliceCheck, Fetch, and Advance (Swift lower camel case). These respectively check processing state, budget exhaustion, the reserved slice count, fetch/increment the instruction pointer, and advance the completed-opcode count. Checks short-circuit in that order; a terminal loop attempt may therefore have no instruction. Execute begins through InstructionStarting, not a separate PhaseStarting callback. It includes opcode dispatch, synchronous callbacks and any fault handling before FinishSlice.

The VM calls InstructionStarting(address) / instructionStarting(address) once before executing each fetched opcode, with its zero-based address before jumps or calls change the instruction pointer. This counts instruction starts, including an opcode that later fails; it need not equal ExecutionResult’s completed-opcode counter after a fault. FinishSlice() / finishSlice() closes the last interval on completion, pause, limit or error. It can occur without an instruction and must clear pending timing. A resumed slice starts a fresh interval. Initializations use the same instrumentation; native message handlers do not.

Callbacks are observational, synchronous and serialized with the owning Host; they must not throw, mutate execution, or reenter any Host. A shared factory across Hosts requires embedding-provided serialization. Violations are outside execution and recovery guarantees. Timing, storage and report generation belong to the embedding, not to the portable runtime. No clock access, source mapping or profiling allocation occurs when instrumentation is omitted. The C# loop uses six optional callback checks per completed instruction, checks on the terminal loop attempt, and a finish check per slice. Swift selects a specialized instrumented or uninstrumented loop once per slice, sharing the opcode implementation.

A phase-based timer ends the previous phase at callback entry, updates its preallocated counters, and starts the next interval after bookkeeping. It flushes the last phase at FinishSlice. Pre-execution phases are attributed to the following instruction; terminal checks without an instruction remain in phase totals only. This excludes bookkeeping and time outside slices, but includes callback/clock overhead, loop transitions, and synchronous work performed by an instruction. Call instructions measure the call mechanism; callee opcodes receive their own exclusive measurements. These are instrumented wall-time observations, not portable timing guarantees or uninstrumented opcode costs.

ExecutionResult

Since: 0.2.0 — Waiting result

ExecutionResult is an allocation-free value containing state, opcodes executed, logical messages processed, messages emitted, messages published, and an optional first diagnostic. Counters cover only the current pump call.

The states are:

  • Paused: a script handler remains resumable because ExecuteFrame exhausted its scheduler opcode budget;
  • Completed: the pump reached idle without a stopping limit/error;
  • Waiting: no runnable work remains but delayed messages are pending; the caller may wait for NextMessageDelay and pump again;
  • RuntimeLimitReached: a configured safety limit stopped the pump;
  • RuntimeError: at least one runtime diagnostic occurred during the pump; the Host still completes the remaining captured dispatch work unless another stopping limit is reached.

The Host remains reusable after a limit or runtime error as specified by HostRuntime. Native callbacks are atomic and do not consume scheduler opcodes.

Random generator API

RandomGenerator is mutable identity-owned state. Create() chooses a nondeterministic seed; FromSeed(Int64) uses the exact portable seed; FromSequence(values) copies a finite-length Binary64 sequence and continues with a private generator after exhaustion. The 32-bit seed overload is an exact widening convenience.

FromEntropy(bytes) folds a non-empty byte span into a deterministic seed with the portable, endian-neutral mixing function, so identical bytes reproduce a run across ports; it equals FromSeed(SeedFromEntropy(bytes)). SeedFromEntropy(bytes) exposes that fold so one derived seed can configure several generators or hosts. Acquiring default entropy for Create() and the seedless host builder is platform-specific and outside the deterministic contract. The exact mixing function and known-answer vectors are in Determinism.

NextInclusiveInteger(first, second) samples uniformly over the inclusive signed-64 range after ordering the bounds. NextFloat(first, second) uses a [0,1) source and Binary64 scaling; final rounding may produce the upper bound. Equal and NaN bounds consume no stream value. Exact transitions and known-answer vectors are in Determinism.

Push(seed) starts a child scope from that seed. Push() clones the current stream into a child scope. Pop() restores the parent stream. The Boolean result reports whether the requested transition was accepted. Host-owned generators add private runtime boundary and fault-gate behavior from HostRuntime. Runtime markers are deliberately not public API; only Host can fence callbacks.

A RandomGenerator is not thread-safe. A Host never accepts a live generator from outside, preventing cross-Host random-state interference.

Extension API

ExtensionRegistry and references

ExtensionReference is immutable normalized (extensionName, functionName, orderedArgumentLabels) data with a canonical SignatureId. It copies labels; neither caller-owned input nor a returned collection view may change them. Collection adapters must not expose mutable backing storage, and label enumeration must remain consistent with SignatureId for the reference lifetime.

ExtensionRegistry.Resolve(reference) -> Optional<ExtensionFunction> is called during Host.Load, not for each invocation. Absence causes a stable link failure. The registry must resolve equal references consistently during one Load and must not depend on culture or reflection in portable Core.

ExtensionFunction.Invoke(call) is a synchronous trusted callback. Host/VM establishes a random boundary before invoking it and restores that boundary after return, including imbalance and limit handling.

ExtensionCall

ExtensionCall is a reusable call-scoped mutable adapter. During Invoke it exposes borrowed ordered Arguments, Host Context, Random, and RuntimeLimits. Exactly one result may be selected through SetNothing, SetValue, SetBoolean, SetInteger, SetFloat, SetPercentage, SetText, or SetTag; the last setter call is the effective result. Omitting a result produces Nothing.

The callback must not retain the call, arguments, Context, or borrowed values. Setters are invalid outside an active invocation. Implementations should reuse the adapter and must not allocate it per hot-path invocation after warmup. Invoke may throw ExtensionFaultException or an unanticipated exception; see “Diagnostics and failure transport” for the resulting contract.

External-type API

Declarative catalog

ExternalTypeDefinition is immutable normalized data containing a type name, ordered unique fields, and ordered unique constructor signatures. ExternalTypeFieldDefinition and ExternalTypeParameterDefinition each contain a normalized name and exactly one portable type description: builtin kind plus unit, or custom type name. ExternalTypeConstructorDefinition contains type name, ordered unique parameters, and derived SignatureId.

A Vector or Point declaration without a unit constraint preserves the supplied spatial value’s unit. An explicit non-none unit overrides that unit without rescaling components. This applies equally to constructor arguments, direct field reads, and fields materialized by conversion to Map. A missing spatial unit constraint must never silently strip an existing unit.

Construction copies sequences and rejects null entries, duplicate fields, duplicate constructor signatures, duplicate parameter labels, constructors for another type, and constructor parameters that are not declared fields. The resulting field, constructor, and parameter views must not expose mutable backing storage through collection adapters or auxiliary collection APIs.

ExternalTypeCatalog.Types is immutable declaration order. Resolve(typeName) normalizes and returns a definition or absence. Catalog construction rejects duplicate type names. The catalog contains no constructor, field accessor, delegate, reflection object, or host instance. Its public enumeration and name lookup must continue to describe the same definitions after construction; accessing a collection view cannot alter either.

Runtime bindings

ExternalTypeConstructorReference is immutable normalized type name plus ordered argument labels and canonical SignatureId. It copies labels and exposes only immutable views, so caller mutations and collection adapters cannot change the reference or make its labels disagree with SignatureId. CreateSignatureId is a pure canonical formatter.

ExternalTypeRegistry.Resolve(reference) -> Optional<ExternalTypeConstructor> runs during Host.Load. Absence or a returned constructor whose Definition does not exactly match the import causes a stable link failure.

ExternalTypeConstructor.Invoke(call) is synchronous. The call exposes borrowed ordered Arguments. SetExternalValue(value) accepts only the expected declared type; mismatch becomes a runtime diagnostic and Nothing. SetNothing() rejects construction without throwing a script exception. Omitting a result also yields Nothing. The call object and arguments must not be retained.

ExternalValue.Definition identifies the declarative type. GetField(name) returns a Value or absence. It must be synchronous and deterministic for an immutable logical external value during one handler. Platform object identity, reflection, and lifetime management are adapter responsibilities.

Diagnostics and failure transport

Diagnostic is immutable structured data containing phase, stable ASCII code, human message, optional symbol and symbol kind, optional SourceLocation, optional program/handler identity, and optional technical details. SourceLocation contains optional source ID, source/module names, and one-based Unicode-scalar line/column bounds. Missing context is explicit absence.

Diagnostic phases are Parse, Validate, Compile, Decode, Link, and Runtime. SymbolKind classifies unknown, type, predicate, function, handler, message, variable, and global definition. DiagnosticCodes exposes stable named constants; Decode(formatErrorCode) maps a .gesb format failure to its stable decode code.

CompileException, DynamicLinkException, ProgramFormatException, and the abstract fatal-runtime base are C# failure transports carrying diagnostics. A language may use thrown errors or result unions. Callers must be able to inspect the same structured portable fields without parsing text. Runtime diagnostics normally flow through Observer and ExecutionResult rather than escaping callbacks; the fatal-runtime base is for trusted native integration and does not define a script-visible exception mechanism.

ExtensionFaultException extends the fatal-runtime base. An extension function, external-type constructor, or ExternalValue.GetField implementation constructs and throws it to deliberately report a defined failure with a caller-chosen code, message, and optional symbol; the code must not use the reserved runtime. prefix owned by DiagnosticCodes. The C# constructor rejects null codes or messages, and empty or whitespace-only codes. An optional inner cause may be supplied; it is never part of the diagnostic’s normative phase, code, or message, only of its non-normative technical details. Any other exception from the same three boundaries is unanticipated and is reported instead with the matching runtime.extensionCallFailed, runtime.externalConstructorFailed, or runtime.externalFieldAccessFailed code. Both outcomes end only the current handler with a diagnosable runtime error; the host remains reusable, and neither outcome evaluates to Nothing.

Native handlers may also use this failure transport; their unexpected exceptions use runtime.nativeHandlerFailure, as defined in Diagnostics.

C# Reflection adapters forward the original exception thrown by an attributed constructor, factory method, or property getter. Wrappers introduced by the Bridge’s own Reflection invocation must not change whether the callback reports a deliberate or unexpected fault. This guarantee does not unwrap exceptions originating inside application code: if a callback uses its own Reflection call and lets a TargetInvocationException escape, that wrapper is the unexpected exception reported at the callback boundary, even if its inner cause is an ExtensionFaultException. The cause remains available in technical details.

Conformance parser API

The independent Python runner owns Markdown parsing and normalized test inputs. See MarkdownFormat.md.

Conformance runner API

The runner owns assertions and communicates with implementation adapters through the V1 process protocol. There is no public C# Conformance library API.

Conformance writer API

Reports and Received Markdown are produced by the independent runner. Their contracts remain in Runner.md.

Language-binding requirements

C# and Unity

GameEventScript.Runtime supplies Runtime. The optional GameEventScript.Compiler package supplies GameEventScriptBuilder, GameEventScriptCompileOptions, and GameEventScriptCompileException in the GameEventScript.Api namespace. Applications compile through GameEventScriptBuilder.Create(), construct hosts through GameEventScriptHost.CreateBuilder(), and load precompiled Programs through the Reader.

Package and assembly identities use GameEventScript.Runtime, GameEventScript.Compiler, GameEventScript.CSharpBridge, and GameEventScript.SyntaxHighlighter. Their shared C# namespace root is GameEventScript; the module name does not add another namespace layer to the public GameEventScript.Api contracts. Public type names remain unchanged.

GameEventScript.CSharpBridge depends only on Runtime and must not pull in Compiler or Conformance, directly or transitively. It supplies:

  • GameEventScriptCSharpValue.GesMap(entries) and GameEventScriptCSharpValue.GesRecord(typeName, fields) snapshot dictionary entries and delegate to the portable ordered value factories. Null or empty dictionaries mean no entries; dictionary enumeration order does not determine the resulting canonical key order;
  • tuple/dictionary message and Context conveniences;
  • delegate native-handler adapters;
  • reflection/attribute extension and external-type registries;
  • an optional lock-based automatic Host runner with the ownership-transfer and scheduling rules in HostRuntime.

Source-file reading belongs to the CLI or embedding. The compiler accepts AddScript(text, sourceName); CSharpBridge has no compiler-builder extensions.

These adapters must delegate to the portable semantics. Unity consumes the C# DLL and may choose main-thread/manual pumping instead of the automatic runner.

Swift

GesValue, GameEventScriptMessage and GameEventScriptMessageSignature retain immutable public value semantics. Read-only properties may use shared immutable backing storage; copying a value and replacing a VM register never mutates another copy. Native register layout is an implementation detail and does not define binary or JSON encoding.

Swift message arguments retain ordered signature labels and values in separate immutable arrays. Their Iterator yields GameEventScriptMessageArgument pairs without creating an intermediate pair array. Returned arrays retain Swift copy-on-write semantics; caller mutations cannot change a message.

The Swift mapping separates the GameEventScriptRuntime, GameEventScriptCompiler, and GameEventScriptConformance SwiftPM packages. Compiler depends only on Runtime; Conformance depends on both. Runtime has no dependency on Compiler, a test framework, or Conformance.

GameEventScriptBuilder.addScript(_:sourceName:) accepts source text and assigns an independent source ID to each addition, even when diagnostic names repeat. The builder supports withDebugInfo, withProgramVersion, and a declarative withExternalTypeCatalog. compile(options:) returns a reusable immutable Program; repeated compilation does not mutate earlier results. A supplied GameEventScriptCompileOptions overrides the builder’s options for that call. GameEventScriptDebugInfoOptions independently selects symbols, source maps, and source archives; .all is the default and .none omits all three. Unknown option bits are invalid public arguments. Source diagnostics are thrown as GameEventScriptCompileError with ordered GameEventScriptDiagnostic values. Compiler internals and the compiler-only Runtime SPI are not public embedding APIs.

Runtime exposes GesValue, GesUnit, immutable range descriptors, messages, signatures, Program data and codecs, GameEventScriptHost, Context, lifecycle handles, random generators, diagnostics, and the extension/external-type protocols. GameEventScriptHost.createBuilder() provides the same mutable configuration workflow as C#, including seed, entropy, sequence, limits, observer, sink and registries. Every build() creates an independent Host. The direct initializer remains available. GameEventScriptBuilder.create() creates a compiler builder. load, start, isReady, subscribe, receive, executeFrame, and runToCompletion retain the host contracts above. The mutable VM is host-private.

Program segments use let properties and native Array values with copy-on-write storage; callers cannot mutate a Program through a returned array. Int64, UInt64, UInt16, and Double retain their specified transport widths. Instructions expose numeric words and payload bits, independently of Swift enum memory layout. GameEventScriptProgramReader.read, GameEventScriptProgramWriter.bytes, encodedSize, write(_:into:), GameEventScriptProgramValidator.validate, and GameEventScriptProgramDumper.dump are synchronous and accept/return memory. Reader options retain the three portable retention modes and bounded read limits.

Invalid public construction uses structured throws errors. Signature message creation returns an optional for mismatched arity. Decode failures use GameEventScriptProgramFormatError; linking uses GameEventScriptDynamicLinkError; execution results carry GameEventScriptDiagnostic. Explicit callback faults use GameEventScriptExtensionFault; unexpected errors receive the owning runtime boundary’s stable classification. Call objects and borrowed argument views must not outlive their synchronous callback.

Strict value equality and hashing preserve kind, unit, and Unicode-scalar identity. materializedMap() reads immutable Map/Record contents or lazily materializes declared external fields, propagating field failures. It does not perform a source-language cast. The random generator has seed, sequence, and entropy initializers; each Host constructs its own generator.

Swift Conformance uses the independent Python runner and an internal executable adapter. The adapter returns raw compiler/runtime observations and optional profile measurements, without parsing Markdown or evaluating expectations. ges-conformance-probe provides codec exchange and binary-fuzz entry points. Neither executable is a public library API. Runtime-only verification supplies hash-bound C# Programs to the adapter and reports their compiler identity separately.

Native Swift adapters

The optional GameEventScriptSwiftBridge library depends on Runtime and an internal compile-time macro target. Only that macro target depends on SwiftSyntax; the generated bindings have no SwiftSyntax runtime dependency. Compiler and Runtime must not acquire a Bridge dependency. Its native tests may use a separate compiler consumer, but the Bridge product has no Compiler or Conformance dependency. These adapters do not add portable Core capabilities.

Closure-based native handlers, publish sinks and extension functions delegate to the Runtime protocols. They preserve ordered signatures, tags, lifecycle, synchronous argument borrowing and the existing callback error classifications. Extension registries reject duplicate signatures and optionally resolve missing signatures through an explicit fallback registry. Dictionary message binding requires a known signature with exclusively named parameters and exactly one value for each normalized label. Ordered tuple inputs support positional labels.

GameEventScriptSwiftValueConvertible defines explicit native conversion. Built-in conformances cover Boolean, Text, signed and unsigned fixed-width Swift integers, Float/Double, Optional, Array, String-keyed Dictionary and GesValue. Native decoding checks kind and units, rejects fractional or out-of-range integer conversion and floating-point precision loss. These conversions do not invoke source-language casts. Native integer encoding and decoding are bounded by both Int64 and the requested Swift integer type. Floating-point encoding uses the portable value factory’s NaN, infinity, zero and integral-storage normalization. Nothing represents nil; GesValue conversion is identity. Collection conversions recurse and preserve canonical GES ordering. Dictionary decoding rejects distinct scalar keys that Swift String equality would merge; encoding preserves only keys present in the input dictionary. Native failures use GameEventScriptSwiftConversionError.

GameEventScriptSwiftType<Root> exposes only its declared KeyPath/getter fields and constructor closures. Constructor parameter labels refer to declared fields and inherit their type definitions. Call arguments follow the constructor’s declared parameter order. A type-erased binding supplies a validated compiler catalog and a separate executable constructor registry. Programs contain only the declarative transport data. Wrapped class roots preserve native identity; struct roots follow Swift value semantics. Unwrapping requires the exact binding descriptor that created the external value. Runtime field coercion and map materialization retain the external-value contract above.

Since: 0.2.0 — Swift binding macros and unit conversion helpers

@GesType generates a throwing static createGesType() factory on a nongeneric struct or final class. Each invocation creates a distinct descriptor; callers retain and reuse it for registry construction, wrapping and unwrapping. The optional type name defaults to the Swift name. @GesField exposes only the annotated stored or computed instance property, using its name unless overridden. Fields require explicit Swift type annotations. Standard numeric types infer Number, Bool infers Boolean, String infers Text, Array infers List and Dictionary infers Map; Optional delegates to its wrapped type. Aliases, custom convertible types and GesValue require explicit typeName. Swift compiler type checking still requires the existing strict value-conversion conformances.

@GesField(unit:) may instead declare a numeric quantity in meters, seconds or degrees. It cannot be combined with typeName or a nonnumeric field. Encoding attaches the explicit unit to a native numeric magnitude; constructor decoding checks the exact unit before stripping it and invoking strict native conversion. Nothing retains existing Optional/NaN semantics. No magnitude scaling, truncation or implicit reinterpretation of an already unit-bearing value occurs. These operations are also exposed by GameEventScriptSwiftValue.encode(_:unit:) and decode(_:as:unit:); .none delegates to the ordinary strict conversion.

@GesConstruct exposes a synchronous, nonfailable initializer or static factory returning the annotated type. Throwing callbacks preserve normal classification. Each parameter maps first by external label to an exposed field, otherwise by internal name to an annotated Swift property. Declaration order defines the callback’s argument order. Defaults do not add overloads; unmarked constructors remain unavailable to scripts. Async, variadic, inout, ownership-qualified and generic constructors are rejected. Conditional #if field/constructor declarations require manual bindings. Annotation misuse is a Swift compile error; portable descriptor name/signature validation still occurs when the throwing factory executes. Manual descriptors remain available for foreign/generic types and throwing getters. Macro expansion adds no GES syntax or portable behavior.

GameEventScriptSwiftHostRunner is an optional synchronized embedding adapter. It takes exclusive ownership through Swift 6 sending parameters and serializes Host access and lifecycle operations with one recursive lock. Transferred Host, callback and external-value state must not be accessed outside that ownership domain. Its explicit start() runs the initial group synchronously. Accepted receives and later loads schedule work on a shared serial background dispatcher, as in the C# bridge. An already ready transferred Host schedules preexisting work; a loading Host waits for Start. Immutable Programs remain reusable without exclusive ownership transfer. Recursive receives only enqueue; recursive pumping fails. runToCompletion() can drain a ready Host synchronously under the same gate. Automatic pumps continue scheduling while ready queued work remains. lastResult is a synchronized snapshot of the most recent completed pump. isReady reports the startup barrier; a registration’s startResult reports its instance initialization. C# exposes the same outcome through runner.GetStartResult(instance). Runner-owned handles use stable registration IDs, retain their runner and detach idempotently under its lock. Closing rejects further work, detaches owned registrations and releases the Host; an already executing pump may finish. Blocking on another caller while inside a callback is outside this contract.

Portable language mappings

Ports should prefer native immutable collection views, nullable/optional result types, and their standard error transport. They must retain:

  • ordered message arguments and stable signature IDs;
  • exact fixed-width integer/Binary64 values and .gesb IDs;
  • one serial, non-thread-affine Host execution domain;
  • synchronous callbacks and explicit outer synchronization;
  • idempotent lifecycle handles and enqueue-time snapshots;
  • separate declarative external catalogs and executable runtime registries;
  • fileless byte/text codec and Conformance operations;
  • identical diagnostics, result states, ordering, and canonical writer output.

An actor, coroutine, executor, or mutex wrapper is an embedding adapter. It must not become hidden scheduling inside portable Host operations.

Completeness map

The following map assigns every exported C# reference type to exactly one conceptual public family and owning section. Every public constructor, method, property, field, and event inherits the assignment of its declaring type. The approved C# API snapshot fixes the concrete reference-binding spellings and members; reviewing a snapshot change therefore also requires reviewing the corresponding row below. Numeric enum members and individual normalized Conformance fields are owned by their linked format specifications rather than duplicated as a second source of truth here.

Public family Owning section
Builder, CompileOptions, CompileException Compiler API
Program, all segment/entry/view types, instruction and ID types Program and bytecode data API
BinaryFormat constants, Reader, read options/limits/retention, Writer, Validator, format error, Dumper Program codec, validation, and dump API
Message, MessageArgument(s), MessageSignature Message and value API
Value, ValueSlice, ValueArguments, ValueMap, integer/float range Message and value API
HostBuilder, RuntimeLimits, Host, StartResult/State, Instance, Subscription Host construction and lifecycle API
Context, NativeMessageHandler, PublishSink/Result, Observer, ExecutionResult/State Context, messaging, and execution results
RandomGenerator Random generator API
ExtensionReference, ExtensionRegistry/Function, ExtensionCall Extension API
External definitions/catalog, constructor reference/registry/call, ExternalValue External-type API
Diagnostic, codes/phase/symbol/location and failure transports Diagnostics and failure transport
CSharpBridge reflection, delegate, dictionary, and automatic-runner adapters Language-binding requirements; non-portable

The portable public API is limited to the families above. VM execution state, compiler trees, filesystem services, network clients, task schedulers, and reflection objects remain private or embedding-specific.

Monotonic scheduling API

Since: 0.2.0

C# IGameEventScriptClock.ElapsedMicroseconds and Swift GameEventScriptClock.elapsedMicroseconds provide a borrowed monotonic Int64 microsecond source. Host builders accept WithClock / withClock; absent an injected source, each host uses an independent origin on the system monotonic clock. A shared injected clock is read serially by its embedding.

Host NextMessageDelay / nextMessageDelay returns optional remaining Int64 microseconds without pumping. Waiting / waiting extends execution results when only delayed work remains. The authoritative timing and lifecycle rules are in HostRuntime. C# and Swift automatic runners arm wake-ups outside Runtime, reschedule when new work arrives and cancel wake-ups when closed/disposed.

Explicit product JSON codec

Since: 0.2.0

C# GameEventScriptMessageJson.Serialize/Deserialize and Swift GameEventScriptMessageJson.serialize/deserialize encode/decode messages; SerializeValue/DeserializeValue and serializeValue/deserializeValue handle standalone values. These Runtime-only APIs are synchronous, fileless and Host independent. They implement MessageFormat, including ordered arguments, numeric normalization, limits, data-only reconstruction and explicit external-to-Record export. Encoding may propagate external getter failures. Local message publication never implicitly invokes these APIs.

ConstructData (0xDE) and SplitText (0xDF) extend the public opcode registry. Integral Binary64 range factory inputs normalize to the exact integer range model; see Language. API names that accept Binary64 describe their input representation, not a promise of unnormalized internal storage.

Idle duration API

Since: 0.3.0

C# Host and CSharpHostRunner expose long? IdleDurationMicroseconds; Swift Host and SwiftHostRunner expose Int64? idleDurationMicroseconds. These are read-only observations using the configured monotonic clock. Their lifecycle, reset and absence rules are owned by Host Runtime. Runner reads are serialized through the ownership gate; a disposed/closed runner returns no value. Reading does not schedule work or produce notifications.

Compound collection instruction identifiers

Since: Unreleased — Cartesian opcode

The public bytecode opcode enumeration assigns Cartesian the stable value 0xE0. Its operands and reserved fields are owned by Bytecode. Iterator modes and component output reuse existing instruction words and the UInt16 list pool; no mutable iterator is added to the portable Program API.

GameEventScriptInstructionFlag.Numeric aliases bit 0x40 for Move, Add, Subtract, Multiply, Divide, Power, IntegerDivide, Modulo, Remainder, Min, Max, Negate, Abs and Clamp only. The same bit retains WithTags semantics on result-bearing sends; interpret flags by opcode.