Runtime Semantic Contract¶
TopoExec exposes two related but separate compatibility axes:
- Schema version (
schema_version: 1) describes the accepted graph file shape. - Runtime semantic contract version (
semantic_contract_version: 0.2) describes the meaning of accepted graphs at runtime.
The current semantic contract version is exposed in:
topoexec doctor --format jsonassemantic_contract_version;topoexec doctortext output;topoexec schema dump --format jsonas the schema annotationx-topoexec-semantic_contract_version.
This document is the reference target for future goals that change runtime behavior. A field can be schema-v1 compatible while still changing runtime meaning; such changes must update this contract, docs/43-ci-build-release-tools/versioning.md, tests/goldens, and CHANGELOG.md.
Contract status levels¶
| Status | Meaning |
|---|---|
v0.1 stable |
Semantics shipped with the initial alpha line and should not change in patch releases. |
v0.2 stable |
Semantics stabilized for the next architecture-stabilization alpha. Additive metadata is allowed; existing meaning should not silently change. |
experimental |
Implemented or public but still expected to change before beta. Changes still need docs/tests. |
future extension |
Planned or documented as a non-goal; do not claim implemented behavior. |
Semantic contract table¶
| Semantic | Status | Contract |
|---|---|---|
| Epoch | v0.1 stable |
An epoch is one bounded runtime step, event-loop iteration, or simulated tick. Delay/state/async visibility crosses epoch boundaries. |
| Transaction | v0.2 stable |
A transaction is the set of component executions and staged publications processed under one scheduler decision boundary inside an epoch. Runtime staging prevents recursive downstream calls. |
| Staged publication | v0.1 stable |
GraphContext::publish() and publish_shared() submit output to runtime-owned staging/routing and return a publication result; components must not directly execute downstream components. |
| Commit | v0.1 stable |
Commit is the point where staged publications become visible according to edge kind. Commit metrics count accepted publications that reached their visibility boundary. |
| Immediate edge visibility | v0.1 stable |
immediate edges are same-transaction dependencies in the compiled immediate graph. Nontrivial immediate SCCs are rejected unless exactly owned by a CompositeLoop. |
| Delay edge visibility | v0.1 stable |
delay edges break immediate feedback and make publications visible at the next epoch boundary. |
| State edge visibility | v0.2 stable |
state publications are staged during the current epoch and committed at the next epoch boundary; readers observe the previously committed snapshot during the publishing epoch. |
| Async edge visibility | v0.2 stable |
async publications are deferred to the next epoch boundary and cannot cause same-call-stack recursive execution. policy.max_inflight applies before channel capacity. |
| Trigger readiness ownership | v0.2 stable plus trigger-v2 preview extensions |
Readiness belongs to the runtime trigger engine, not component code. Manual/timer/message/any/all/time-sync/batch/request/task/future-ready paths produce explicit invocation event/trigger metadata. Trigger-v2 watermark, condition, debounce, and rate_limit policies are additive, declarative, and do not execute arbitrary user code. |
| Invocation metadata | experimental |
Correlation, causation, epoch, transaction, source endpoint, and trigger-kind metadata flow through publication, channels, trigger collection, invocations, task completions, and CompositeLoop external commits. Trace events may include these fields; metrics do not use them as default labels. |
| Runtime health events | experimental |
Bounded observer-only health events capture channel overflow/stale/deadline/high-watermark, task reject, and scheduler reject paths. They are exposed through RuntimeRunnerResult, CLI JSON, and trace entries; they do not trigger components or alter scheduling. |
| Channel capacity and overflow | v0.2 stable |
Channels and async admission are bounded. Overflow/drop/reject/deadline/stale outcomes are observable through metrics, health counters, and optional bounded health events, not hidden blocking or recursive control flow. |
| Payload ownership | v0.2 stable |
Built-in text/frame/blob/opaque payloads use explicit copy/shared/loaned/move policies. Large copy and multi-reader move-only misuse are rejected or degraded with observable diagnostics/metrics. |
| CompositeLoop ownership | v0.1 stable |
A CompositeLoop owns an immediate cyclic SCC only when its component set exactly matches the SCC. Partial loop declarations are invalid. |
| CompositeLoop external output commit | v0.2 stable |
External outputs from loop-owned components are staged until the loop region finishes successfully. Internal failure suppresses half-updated external commits and records loop error evidence. solver_iteration defaults to discarding non-converged partial outputs unless partial_success: commit_outputs explicitly allows them. |
| Scheduler event-loop lane | v0.1 stable |
event_loop is the deterministic default lane. It executes compiled ready work with runtime priority ordering for independent ready regions and without OS scheduler claims. |
| Scheduler fixed-rate lane | experimental |
Current behavior is deterministic/simulated over bounded runtime ticks by default, with runtime priority ordering for independent ready regions and opt-in cooperative wall-clock sleeping via wall_clock_enabled, period_ms/hz, and overrun_policy. Tick, overrun, skipped, jitter, blocked-duration, and max-lateness metrics are observable. Independent lane threads, OS jitter control, and hard real-time guarantees are future work. |
| Scheduler thread-pool lane | experimental |
Current behavior is run-scoped persistent worker-pool execution for ready invocations with bounded runtime-priority queue admission, cooperative cancellation/timeout-budget observation, non-reentrant serialization, worker-id trace attributes, and queue/worker/priority metrics. OS scheduling policy, hard timeout preemption, and advanced starvation aging are future work. |
| Runtime metrics | v0.2 stable |
RuntimeRunnerResult::runtime_metrics and CLI metrics JSON expose counters/gauges/histograms for runtime decisions. Metric schema version 1 defines descriptor kind/unit/label/cardinality rules and forbids high-cardinality default labels such as correlation ids. |
| Runtime trace | v0.2 stable |
RuntimeRunnerResult::trace, CLI trace JSON, and Chrome trace output expose trace schema version 1 with ordered timeline fields, phase, component/channel/lane/worker identifiers, and epoch/transaction/correlation/causation metadata. The OTel target provides only a dependency-free preview mapping; production exporters remain future work. |
| Structured runtime errors | v0.2 stable |
RuntimeRunnerResult::runtime_errors records phase/component/lane/code/message/trace/fatality while preserving legacy errors strings. |
| Graph input loading | v0.2 stable |
YAML graph input is bounded by GraphInputLimits: file/text bytes, counts, id/string sizes, config depth/value sizes, and UTF-8 validity fail before runtime execution with diagnostic errors. CLI overrides are per-invocation and do not change runtime semantics. |
| Hierarchical graph expansion | v0.2 stable |
subgraphs[] are compile-time namespace expansions into flat GraphSpec components, edges, CompositeLoops, and GraphHierarchyEntry metadata. Validation and runtime execution run after expansion; hierarchy cannot hide immediate cycles or create nested schedulers. |
| Graph template expansion | v0.2 stable |
templates[] plus template_instances[] perform strict scalar {{parameter}} substitution and then expand through the same flat namespace path as subgraphs. Runtime execution receives only expanded components/edges and no template interpreter. |
| State/config snapshot stores | experimental |
Stores are epoch-boundary committed and observable; component config hot reload uses validate/apply transactions with rollback/fail-fast semantics. |
| Component reset/snapshot/restore | experimental |
Components may opt into start-epoch reset/restore and post-run snapshot capture through RuntimeRunnerOptions; hooks do not interleave with component execution, and pause/resume policy remains future work. |
| Task executor deterministic/threaded helpers | experimental |
ITaskExecutor is the embedder interface, DeterministicTaskExecutor is the deterministic path, and ThreadedTaskExecutor is an opt-in bounded worker preview. Pending cancellation is cooperative, active work is not forcibly killed, completion callbacks route through publisher/channel boundaries, and threaded details may change before beta. |
| Cooperative cancellation and timeout | experimental |
CancellationToken/CancellationSource, GraphContext::cancel_requested(), Invocation::cancel_requested(), component timeout-budget metrics, CompositeLoop between-iteration cancellation, and task-executor pending-task cancellation are implemented. No hard thread termination or timeout preemption is implemented. |
| Runtime observer API | v0.2 stable |
RuntimeRunnerOptions::observers delivers best-effort result/metric/trace/health/error records after run assembly; observer failures are non-fatal diagnostics. Exporter adapters should consume this surface; telemetry preview targets prove the shape with dependency-free mappings. |
| Graph diagnostics | v0.2 stable |
Diagnostic schema version 1 defines code/severity/category/path/involved-id/suggested-fix fields. Warning diagnostics do not fail validation unless CLI strict diagnostics are requested. |
Schema v1 relationship¶
Schema v1 controls graph shape and strict field validation. The semantic contract controls runtime meaning. Compatibility rules:
- A schema-v1 additive field is allowed only when old graphs keep the same runtime meaning.
- Changing the meaning of an existing schema-v1 field requires a semantic-contract update and may require schema v2 even if the JSON/YAML shape is unchanged.
- A graph accepted under schema v1 must not silently acquire unsafe feedback, unbounded backlog, hidden recursion, or adapter-specific behavior.
- Schema annotations such as
x-topoexec-semantic_contract_versiondescribe the runtime contract and are not graph input fields. - CLI JSON and C++ result fields are part of the observable contract when they describe semantic behavior.
Change protocol¶
For any semantic change:
- Update this document and mark the affected semantic status.
- Update
docs/43-ci-build-release-tools/versioning.mdandCHANGELOG.md. - Add or update focused tests, schema/golden checks, or runtime invariant coverage.
- Preserve schema v1 compatibility or explicitly open schema v2 design.
- Keep deferred behavior explicit; do not use optimistic docs to imply implementation.