Simulation Semantics V1
Status
Document status: Current alpha contract and target semantic decisions
Applies to: ABMForge 0.3.0a1 development line
Stability: Public-alpha semantic baseline
Audience: Model authors, framework contributors, reviewers, and plugin authors
This document defines the observable simulation semantics of ABMForge.
It distinguishes between:
- Current behavior — behavior implemented by the current runtime.
- Target contract — behavior ABMForge intends to guarantee after the corresponding semantic corrections are implemented.
- Unsupported behavior — behavior that users must not currently assume.
This distinction is important because documenting an existing implementation does not automatically make every existing behavior desirable or stable.
Related documentation:
- Scheduling
- Delayed Events
- Agent Lifecycle
- Model Lifecycle
- Recording Data
- Reproducibility Tiers
- Replay and Snapshots
1. Scope
Simulation semantics include:
- model step and time progression;
- event processing order;
- agent activation order;
- agent creation and removal visibility;
- simultaneous and staged state transitions;
- recording time;
- stop-condition evaluation;
- random-number ownership;
- failure and partial-step behavior.
This document does not define:
- domain-specific model validity;
- calibration or statistical inference methods;
- continuous-time simulation;
- distributed simulation;
- full checkpoint/resume;
- executable event replay;
- bit-identical results across platforms or dependency versions.
2. Terminology
Step
A step is one completed iteration of the fixed-step model loop.
The model exposes the completed-step count as:
model.steps
The initial value is 0.
Simulation time
Simulation time is exposed as:
model.time
The initial value is 0.0.
The current fixed-step runner increases time by 1.0 after each completed model step.
Tick
In the current fixed-step execution profile, a tick corresponds to one model step and one 1.0 increase in simulation time.
ABMForge does not currently provide a separate tick object.
Activation pass
An activation pass is one scheduler or collection bulk-operation call over a selected set of agents.
Examples:
model.agents.do("step")
model.agents.shuffle_do("step")
model.scheduler.step()
Phase
A phase is an ordered part of a model iteration, such as event processing, model activation, recording, or stop-condition evaluation.
Eligible agent
An eligible agent is an agent permitted to receive a callback in a particular activation pass or stage.
Eligibility depends on:
- membership policy;
- lifecycle state;
- when the activation snapshot was created;
- the selected scheduler.
Current state and next state
For synchronous models:
- current state is the state visible during the decision phase;
- next state is pending state intended to become current during a later commit phase.
The current runtime does not automatically isolate current and next state.
3. Current fixed-step model loop
3.1 Current runtime order
For each requested model step, the current runtime performs:
1. Process events due at the current model time
2. Call model.step()
3. Increment model.steps by 1
4. Increment model.time by 1.0
5. Collect registered model and agent observations
Equivalent pseudocode:
for _ in range(requested_steps):
if not model.running:
break
model.events.process_due(time=model.time)
model.step()
model.steps += 1
model.time += 1.0
model.record.collect()
3.2 Consequences
Under the current implementation:
- events due at time
tare processed beforemodel.step()for timet; - observations are collected after the model step;
- observations use the incremented
stepsandtimevalues; - the first automatic post-step observation is associated with step
1and time1.0; - no automatic time-zero observation is collected;
- an exception during event processing,
model.step(), or recording marks the model as failed; - if
model.stop()is called insidemodel.step(), the current loop still increments the counters and performs the post-step recording for that iteration unless an exception interrupts execution.
3.3 Target fixed-step phase contract
The target fixed-step execution profile will use explicit named phases:
TICK_START
→ PRE_STEP_EVENTS
→ ACTIVATION
→ COUNTER_ADVANCE
→ POST_STEP_OBSERVATION
→ STOP_EVALUATION
→ TICK_END
The exact placement of post-step events, if introduced, must be specified by a later version of this contract.
No plugin or scheduler may silently reorder these phases.
4. Event-time semantics
4.1 Current fixed-step contract
The built-in fixed-step execution profile accepts only finite, integer-valued event ticks.
Events may be scheduled using:
model.events.schedule_at(time, callback=...)
model.events.schedule_after(delay, callback=...)
Accepted values include integers and integer-valued floats:
model.events.schedule_at(2, callback=callback)
model.events.schedule_at(2.0, callback=callback)
model.events.schedule_after(1, callback=callback)
model.events.schedule_after(1.0, callback=callback)
Accepted values are normalized to float for queue storage and ordering.
The current contract rejects:
- fractional absolute event times;
- fractional delays;
- Boolean values;
- strings and objects accepted only through implicit numeric coercion;
- non-finite values;
- negative delays;
- absolute event times earlier than the current model time.
A rejected scheduling request does not:
- consume an event identifier;
- create a pending event;
- write an event record.
4.2 Execution timing
For an event scheduled at tick t, the fixed-step runner processes the
event before model.step() when model.time == t.
Pending events are ordered by:
event tick
-> priority
-> insertion sequence
-> event identifier
process_due(time=t) executes pending events whose scheduled tick is less
than or equal to t.
Because fractional event times are rejected during scheduling, the built-in runner cannot silently delay a fractional event until the next integer model time.
The fixed-step profile is not a continuous-time or general discrete-event simulation engine.
4.3 Same-tick event behavior
During process_due(time=t), an event callback may schedule another event
for tick t.
Because the queue continues draining while due events remain, the newly scheduled event may execute during the same event-processing call.
An event scheduled for the current tick from inside model.step() is
created after the pre-step event drain. It therefore executes during a
later event-processing phase.
This callback-context distinction is current behavior.
4.4 Remaining event-system work
Future event-system revisions should:
- distinguish event creation, requested execution, and actual execution timestamps in event records;
- define a resource-safety policy for recursive same-tick scheduling;
- formalize any post-step event phase;
- version the event-time and event-phase policies in run metadata;
- use a separate experimental profile for hybrid or continuous-time simulation.
5. Agent collection bulk operations
5.1 Shared callback-time eligibility
All built-in collection bulk operations and schedulers use a shared callback-time activation eligibility rule.
A candidate agent is eligible immediately before a callback only when:
- the collection still stores an object under the agent identifier;
- the collection still stores the same object under that identifier;
- the candidate agent remains alive.
Candidate agents are selected from a snapshot created at the beginning of the activation pass or scheduler call.
Consequences:
- an agent added during the pass is deferred until a future pass;
- an agent removed before its turn is skipped;
- an agent marked not alive before its turn is skipped;
- if an agent is removed and replaced by another object with the same identifier, neither the removed snapshot object nor the newly added replacement is activated from that snapshot position;
- self-removal is safe and prevents callbacks in later phases or stages.
This rule prevents stale objects retained by an activation snapshot from being called after removal.
5.2 AgentCollection.do
model.agents.do("step")
do() creates an insertion-ordered candidate snapshot at the beginning of the
call.
Immediately before each callback, it revalidates object identity, current collection membership, and lifecycle eligibility.
Insertion order remains part of the activation semantics.
5.3 AgentCollection.shuffle_do
model.agents.shuffle_do("step")
shuffle_do() creates one candidate snapshot at the beginning of the call and
shuffles its indices using the named scheduler stream returned by
model.rng_stream("scheduler").
Immediately before each callback, it applies the same identity, membership,
and lifecycle eligibility checks as do().
An agent removed after the permutation is generated is skipped when its turn is reached.
5.4 Scope of the guarantee
Callback-time eligibility determines whether a candidate receives an activation callback.
AgentCollection.remove(...) and Model.remove_agent(...) both perform
managed lifecycle removal, including spatial cleanup, owned-event
cancellation, collection removal, lifecycle transition, and lifecycle
recording.
space.remove(...) performs spatial unplacement only and does not alter
collection membership or lifecycle state.
6. Sequential activation
6.1 Current behavior
SequentialActivation:
- creates an insertion-ordered candidate snapshot;
- visits candidates in snapshot order;
- revalidates object identity, collection membership, and
is_aliveimmediately before each callback; - calls
agent.step()only for eligible candidates.
An agent added after the candidate snapshot is created is deferred until a future scheduler call.
An agent removed or marked not alive before its turn is skipped.
If a removed agent is replaced by another object using the same identifier, the old object fails callback-time identity validation and the replacement is not present in the original snapshot.
6.2 Scientific implication
Insertion order is part of the model semantics.
Researchers using sequential activation must document:
- how initial insertion order is determined;
- whether creation order has theoretical meaning;
- whether dynamic creation changes future activation priority;
- whether results are sensitive to activation order.
7. Random activation
7.1 Current behavior
RandomActivation:
- creates a candidate snapshot from currently eligible agents;
- generates a permutation using the named
schedulerstream returned bymodel.rng_stream("scheduler"); - revalidates object identity, collection membership, and
is_aliveimmediately before each callback; - calls
agent.step()only for candidates that remain eligible.
Agents added after candidate selection are deferred.
Agents removed or marked not alive after the permutation is generated are skipped when their turn is reached.
A replacement object using the same identifier is not activated from the removed object's snapshot position.
7.2 Current named-stream contract
Model.rng remains the default behavior stream used by existing model and
agent code.
Model.rng_stream(name) returns a cached named stream. For an explicit model
seed, the stream is deterministically derived from:
- the versioned
named-rng-streams-v1policy namespace; - model-level root material;
- the normalized stream name.
Python's process-randomized hash() function is not used. Stream creation
order does not affect another stream, and consuming the default behavior
stream does not consume the named scheduler stream.
The following built-in paths use the named scheduler stream:
RandomActivation;AgentCollection.shuffle_do();- shuffled
StagedActivation.
Consequently, unrelated draws made through Model.rng or Agent.rng do not
change later built-in scheduler permutations.
Changes that consume the scheduler stream itself can still change later
scheduler order.
7.3 Snapshot and restore contract
Snapshots record:
- the default
rng_state; - the
rng_stream_policy; - model-level
rng_stream_rootmaterial; - opened named stream states in deterministic name order.
Restore validates these fields as one versioned group. Opened named streams continue from their saved states, while streams that had not yet been opened remain derivable from the restored root material.
Legacy snapshots containing only rng_state remain restorable.
When the model seed is None, new model construction uses fresh root material.
A snapshot preserves that root so restored unopened streams remain consistent
with the original model.
8. Simultaneous activation
8.1 Current behavior
SimultaneousActivation:
- creates a candidate snapshot from the model collection;
- validates every initially eligible candidate before the first callback;
- requires callable
step()andadvance()methods from every participating candidate; - reports all invalid candidates in one
TypeErrorand aborts before any activation callback has run; - revalidates every candidate before its
step()callback; - completes all remaining eligible
step()callbacks; - revalidates every candidate before its
advance()callback; - completes all remaining eligible
advance()callbacks.
An agent removed before its decision turn does not receive step().
An agent removed during the decision phase does not receive advance().
An agent added during the scheduler call is deferred until a future call.
A new object inserted under the identifier of a removed snapshot candidate is also deferred until a future scheduler call.
Agents already ineligible when the call begins are excluded from the strict two-phase capability check.
8.2 Current guarantee
validate the complete initially eligible candidate snapshot
-> all remaining eligible step() callbacks
-> all remaining eligible advance() callbacks
Validation occurs before activation begins. A contract error therefore cannot leave the model with only a prefix of agent callbacks executed.
Callback-time identity, membership, and lifecycle eligibility are checked again in both phases.
The current implementation guarantees callable two-phase participation and all-decision-before-any-commit ordering. It does not guarantee:
- immutable current-state views;
- automatic next-state buffers;
- order-independent decision calculations;
- atomic model-wide state commit;
- rollback when an agent callback itself raises an exception.
8.3 Required user pattern
Synchronous models should use explicit pending state:
class ExampleAgent(Agent):
def step(self) -> None:
self.next_value = self.compute_next_value()
def advance(self) -> None:
self.value = self.next_value
Directly changing self.value in step() may expose the updated value to
agents activated later in the same decision phase.
Agents with intentionally empty commit work must provide an explicit callable
no-op advance() method. ABMForge does not silently create commit adapters.
8.4 Public-alpha status
Strict two-phase simultaneous activation is part of the current runtime guarantee.
The resolved contract includes:
- complete capability validation before activation;
- callable
step()andadvance()requirements; - one aggregated error for all invalid initial candidates;
- all-decision-before-any-commit ordering;
- callback-time removal, replacement, and lifecycle checks in both phases.
Automatic current/next-state isolation remains outside the runtime guarantee.
The current canonical Model Zoo models use asynchronous random activation and now have model-specific scientific verification. A future synchronous canonical model must provide its own invariant and metamorphic evidence before it is presented as a verified reference model.
9. Staged activation
9.1 Current behavior
StagedActivation:
- creates a candidate snapshot from currently eligible agents;
- retains that candidate snapshot for all declared stages;
- executes stages in declared order;
- optionally generates a separate permutation for each stage using the
named
schedulerstream returned bymodel.rng_stream("scheduler"); - revalidates object identity, collection membership, and
is_aliveimmediately before every stage callback; - calls optional
before_stage(stage)andafter_stage(stage)model hooks.
9.2 Lifecycle visibility
Under the current implementation:
- agents added after candidate selection do not participate in the current scheduler call;
- agents removed during an earlier stage are skipped in later stages;
- agents removed before their turn in the current stage are skipped;
- agents marked not alive are skipped;
- a same-identifier replacement is deferred because it is not part of the original candidate snapshot;
- each shuffled stage may use a different activation order;
- declared stage methods must exist and be callable.
9.3 Research reporting requirements
A staged model should document:
- stage names and order;
- whether stage-level shuffling is enabled;
- which state changes are visible to later stages;
- whether creation or removal may occur during stages;
- what the model-level stage hooks do;
- which random stream controls stage order.
10. Agent creation and removal
10.1 Same-pass creation
Built-in activation paths create a candidate snapshot at the beginning of their activation pass or scheduler call.
An agent added after that snapshot is created is registered immediately but is not activated until a future pass.
This guarantee applies to:
AgentCollection.do();AgentCollection.shuffle_do();SequentialActivation;RandomActivation;SimultaneousActivation;StagedActivation.
10.2 Same-pass removal
An agent removed before its next callback becomes immediately ineligible.
Eligibility requires:
- current membership in the collection;
- identity equality with the object currently stored under the identifier;
- a living lifecycle state.
This prevents stale candidate references from being called after removal.
10.3 Same-identifier replacement
If an agent is removed and a new object is inserted under the same identifier during an activation pass:
- the removed object fails the callback-time identity check;
- the replacement object is absent from the original candidate snapshot;
- neither object is activated from the stale snapshot position;
- the replacement becomes eligible in a future pass.
10.4 Managed lifecycle removal
AgentCollection.remove(...) and Model.remove_agent(...) use the same
managed lifecycle operation.
The collection accepts only active living agents. Removed or non-living agent objects cannot be added or re-added.
Managed removal:
- validates that the supplied agent is the current collection object;
- removes the agent from its actual space when spatially placed;
- clears spatial position, occupancy, node, and identity indexes;
- clears
agent.posandagent.world; - cancels pending events owned by the agent;
- removes the agent from the collection;
- changes
is_aliveto false and lifecycle status toremoved; - writes one
agent_removedlifecycle record.
An object with the same identifier but a different object is rejected.
Repeated removal raises KeyError and does not write a duplicate lifecycle
record.
10.5 Spatial unplacement
space.remove(...) is spatial unplacement only.
It removes the agent from that space and clears spatial attributes, but does not remove the agent from the model collection, change lifecycle state, cancel owned events, or write lifecycle records.
Built-in spaces clear both agent.pos and agent.world after successful
unplacement.
Built-in spaces also validate object identity for placement, movement, position lookup, and removal. An agent already placed in another space cannot be placed into a second built-in space.
10.6 Failure ordering
Spatial cleanup completes before lifecycle mutation.
If spatial removal raises an exception, collection membership, lifecycle state, owned events, and lifecycle records remain unchanged.
10.7 Public-alpha status
Uniform collection-space lifecycle integrity for the managed model lifecycle and all built-in spaces is now part of the current runtime guarantee.
11. Recording semantics
11.1 Current automatic recording
Registered model and agent observations are collected after:
model.step()
→ model.steps += 1
→ model.time += 1.0
Therefore, an automatic record containing:
step = 1
time = 1.0
describes the post-step state after the first completed model step.
11.2 Time-zero observations
The current model loop does not automatically collect an initial time-zero observation.
Users requiring a baseline observation must collect it explicitly or use a future recording profile that declares an initialization phase.
11.3 Frequency
The every option is evaluated using the current completed-step count.
For example, every=2 records at completed steps divisible by 2.
11.4 Conditional recording
A when predicate is evaluated at recording time, after the model step and counter increment.
Agent-level where predicates are evaluated for agents present in the collection at recording time.
11.5 Event and lifecycle record times
Event and lifecycle transitions are currently recorded using the model’s current steps and time when the recorder method is called.
The event record does not by itself distinguish:
- when the event was created;
- when it was scheduled to run;
- when it became due;
- when it actually executed.
11.6 Target observation contract
A future observation schema will explicitly declare:
- observation phase;
- variable type;
- unit;
- missing-value policy;
- aggregation level;
- whether the observation represents pre-step, post-step, initialization, or finalization state.
12. Stop-condition semantics
12.1 Direct model stop
Calling:
model.stop(reason)
sets:
running = false
status = stopped
stop_reason = reason
A stopped model cannot be restarted through the current fixed-step runner.
12.2 Scenario stop condition
A scenario-level stop_when callback is currently evaluated:
- before each requested model step;
- after each completed one-step model execution.
If the callback is true before the first step:
- the model is stopped;
- no model step is executed for that iteration.
If the callback becomes true after a completed step:
- the step has already run;
- counters have already advanced;
- post-step recording has already occurred;
- the model is then marked stopped.
12.3 Outcome interpretation
stopped means execution ended because of an explicit model or scenario condition.
It does not inherently mean:
- failed;
- invalid;
- incomplete;
- excluded from analysis.
Execution status and scientific analysis eligibility are separate concepts.
The default researcher-report policy treats completed and stopped runs as analysis-eligible when at least one numeric final model metric is available. Failed and non-terminal runs remain visible but are excluded from default metric analysis with an explicit reason in analysis_eligibility.csv.
13. Failure semantics
13.1 Current model-loop failure
An exception during the fixed-step loop:
- stops execution;
- sets the model status to failed;
- re-raises the exception.
The model may already contain partial state changes from:
- processed events;
- partially completed activation;
- the model step;
- partially completed recording.
13.2 No automatic rollback
The current runtime does not provide transactional rollback of a partially executed simulation step.
Users must not assume that a failed step leaves the model at the previous completed state.
13.3 Target failure contract
Future versions should record:
- failure phase;
- current step and time;
- active scheduler stage;
- related agent or event identity where available;
- whether records were committed;
- whether the run is retryable;
- whether the artifact is complete, partial, or corrupt.
14. Randomness and determinism
14.1 Current generators
Model.rng remains the default behavior stream used by existing model and
agent code.
Model.rng_stream(name) provides cached named component streams derived
independently of the default behavior stream and independently of stream
creation order.
Built-in randomized activation uses the named scheduler stream for:
RandomActivation;AgentCollection.shuffle_do();- shuffled
StagedActivation.
The current derivation policy is named-rng-streams-v1.
14.2 Current same-seed guarantee
Under the same code, environment, initial state, execution order, seed, and component-stream draw histories, ABMForge is intended to reproduce the same model trajectory.
Unrelated draws from Model.rng or Agent.rng do not consume the named
scheduler stream and therefore do not change later built-in scheduler
permutations.
The runtime does not guarantee control of:
- Python's global
randommodule; - third-party-library random generators;
- operating-system or dependency-version differences;
- cross-platform bit-identical results;
- byte-identical archives.
14.3 Snapshot and manifest support
Snapshots record the default RNG state, the named-stream policy, root material, and opened named-stream states.
Reproducibility manifests record the named-rng-streams-v1 policy identifier.
15. Current guarantee matrix
| Topic | Current guarantee | Not currently guaranteed |
|---|---|---|
| Fixed-step time | steps += 1 and time += 1.0 after a completed model step |
Variable-step or continuous-time execution |
| Event times | Finite integer-valued ticks with strict input validation | Hybrid or continuous-time execution |
| Event ordering | Time, priority, sequence, and event identifier | Context-independent same-time scheduling semantics |
Collection do() |
Insertion-order candidate snapshot with callback-time identity, membership, and liveness validation | Activation of agents added during the current pass |
Collection shuffle_do() |
Named scheduler-stream permutation with callback-time eligibility validation | Independence from changes that consume the scheduler stream itself |
| Sequential activation | Insertion-order candidate snapshot with callback-time eligibility validation | Dynamic additions during the current pass |
| Random activation | Initial eligible snapshot, named scheduler-stream permutation, callback-time eligibility validation, and isolation from default behavior RNG draws | Independence from changes that consume the scheduler stream itself |
| Simultaneous activation | Preflight validation of callable step() and advance(), followed by all eligible decisions before all eligible commits |
Automatic current/next-state isolation |
| Staged activation | Declared stage order, optional per-stage shuffle, and validation before every stage callback | Dynamic additions during the current scheduler call |
| Same-pass removal | Removed, replaced, or non-living candidates are skipped before their next callback | Reactivation during the existing candidate snapshot |
| Managed lifecycle removal | Collection and model removal share identity-validated built-in-space cleanup, event cancellation, collection removal, lifecycle transition, and recording | Automatic rollback after arbitrary user callback failures |
| Spatial unplacement | Built-in spaces validate object identity, clear indexes, and clear agent.pos and agent.world |
Lifecycle removal from direct space.remove(...) |
| Same-pass creation | Newly added agents are deferred until a future pass | Immediate participation in the current candidate snapshot |
| Recording | Post-step observations use incremented counters | Automatic time-zero observation |
| Scenario stop | Stop condition is checked before and after one-step execution; stopped runs are eligible when numeric final metrics exist | Study-specific scientific validity beyond the default reporting policy |
| Failure | Failed status is recorded and the exception is raised | Transactional rollback of a partial step |
| Snapshot | Selected model and agent state, default RNG state, named-stream root, and opened named stream states are captured | Full world, scheduler object, event callback, and recorder restoration |
| Reproducibility | Conditional same-seed rerun with named scheduler isolation from default behavior RNG draws | Cross-platform or cross-version equality |
16. Public-alpha semantic status
No unresolved public-alpha semantic blockers remain for the current fixed-step execution profile.
Canonical Model Zoo models now include explicit scientific invariant, boundary-case, metamorphic, and same-seed trajectory tests.
The resolved public-alpha semantic baseline includes:
- removal-aware callback eligibility;
- strict integer-tick event scheduling;
- separation of execution status from analysis eligibility;
- strict two-phase simultaneous activation;
- named scheduler random streams;
- uniform collection-space lifecycle integrity;
- scientific reference-model verification for the canonical SIR and Schelling models.
Scientific reference-model verification checks internal conservation, transition, boundary, symmetry, reproducibility, and recording properties. It does not constitute empirical validation or calibration against observed epidemiological or social data.
Future canonical models must add model-specific invariant and metamorphic evidence before being described as scientifically verified reference models.
17. Model-author responsibilities
Model authors remain responsible for documenting and validating their model-specific scientific assumptions:
- document the scheduler and activation order;
- use
AgentCollection.remove(...)orModel.remove_agent(...)for managed lifecycle removal, and usespace.remove(...)only for spatial unplacement; - use finite integer event ticks in the fixed-step profile;
- implement explicit current/next state buffers for simultaneous models;
- record all random sources and avoid untracked third-party randomness;
- define whether stopped runs are scientifically valid;
- add model-specific conservation and transition invariants;
- preserve source, input, dependency, seed, and environment information;
- treat archive validation as artifact validation, not model validation;
- treat generated ODD output as a draft requiring scholarly review.
18. Framework-contributor requirements
Any change to model execution must answer:
- Does the phase order change?
- Does agent eligibility change?
- Does event ordering change?
- Does the random-draw sequence change?
- Does the observation phase change?
- Can run status or stop reason change?
- Can existing model trajectories change?
- Does the snapshot or archive schema change?
- Is a migration or deprecation path required?
- Which characterization, invariant, property, or metamorphic tests prove the contract?
A change that modifies trajectories must be labeled as a scientific behavior change in the changelog and release notes.
19. Versioning policy
Simulation semantics are versioned independently from package marketing language.
A future manifest should record identifiers such as:
execution_profile = fixed-step-v1
event_time_policy = integer-tick-v1
activation_lifecycle_policy = managed-removal-v1
observation_policy = post-step-v1
rng_policy = named-rng-streams-v1
Changing one of these policies may alter scientific results even when the public Python method signatures remain unchanged.
Such changes require:
- explicit release notes;
- regression tests;
- migration guidance;
- updated reference outputs;
- updated reproducibility metadata.
20. Summary
The current ABMForge runtime is best described as:
A fixed-step Python ABM execution profile with finite integer event ticks, pre-step event processing, candidate-snapshot activation, callback-time identity and membership validation, deferred same-pass additions, immediate removal visibility, strict prevalidated two-phase simultaneous activation, post-step observation, multiple built-in activation strategies, managed lifecycle removal, and built-in space referential integrity.
Fractional event times and implicit numeric coercion are rejected during scheduling, preventing events from being silently executed later than requested.
The public-alpha semantic blockers are resolved for the current fixed-step execution profile and canonical Model Zoo models.
The current reference-model evidence includes explicit invariant, boundary-case, metamorphic, recording-consistency, and same-seed trajectory verification.
This evidence does not constitute empirical validation or calibration.
Researchers must report the exact ABMForge version or commit and the model-specific scheduling, event-time, randomness, observation, lifecycle, and scientific-validation assumptions used in their studies.