Constraints
===========

What each execution unit may read, may write, and may not do. This
document is normative; it restates the rules of ``eb-semantics.rst`` in
the axis a modeler actually asks about ("can I do this *here*?") and adds
no new ones. Every clause cites the rule it comes from.

The five units are ``pi``, ``event``, ``agent``, ``fn``, and ``init``,
plus the exec section, which is not an execution unit but does admit
statements.


Periodic items (``pi``)
-----------------------

**May read** — earlier pis' current-period values, in declaration order
(HIST-60); any pi's history via ``[-k]`` (HIST-10); module-level
variables; context values ``:p``/``:i``/``:t``/``:r`` (CTX-10..30); ``fn``
calls (FN-40).

**May write or do** — its own value, and only through the mandatory
terminating expression (PI-20); ephemeral locals (PI-30); ``stop_r``
(SCHED-20); ``:capacity`` (RC-60); trailing keyword statements after the
value is committed (PI-25).

**May not** — be assigned to (PI-40); mutate state variables (STATE-20);
spawn agents (AG-25); ``timeout`` or ``claim`` (AG-80, CLAIM-40).


Events (``event``)
------------------

**May read** — state variables; module-level variables; the arguments
bound at schedule time (SCHED-12); context values; ``fn`` calls.

**May write or do** — mutate state variables (STATE-20); ``sched()``
further events (SCHED-10); spawn agents (AG-25); ``stop_r`` (SCHED-20);
``:capacity`` (RC-60); ephemeral locals (EV-40). Executes atomically when
dequeued (EV-10).

**May not** — suspend: no ``timeout``, no ``claim`` (AG-80, CLAIM-40);
write module-level variables; read a pi's value (EV-35); be invoked
directly by name (EV-25).


Agents (``agent``)
------------------

**May read** — everything an event may read, plus the arguments bound at
spawn time and ``rsc`` parameters (RC-40).

**May write or do** — everything an event may do, plus ``timeout``
(AG-80), ``claim`` and ``release`` (CLAIM-10, CLAIM-15). Ephemeral locals
(AG-40).

**May not** — write module-level variables (AG-50); read a pi's value
(AG-55); return a value — an agent invocation is a statement, not an
expression (AG-60).

.. admonition:: Suspension is not restricted to the top level

   An earlier specification asserted that suspension could occur only at
   the top level of an agent body, and used that to argue implementations
   need no coroutines. That restriction is **not** part of this language.
   ``timeout`` and ``claim`` are valid wherever a statement is valid
   inside an agent, including within ``if``, ``match``, and ``for``. The
   segment model accommodates this directly: an agent is a cyclic graph
   of segments cut at suspension points, not a straight-line sequence, so
   a suspension inside a loop is an ordinary back edge.


Functions (``fn``)
------------------

**May read** — its parameters and module-level variables. Nothing else.

**May write or do** — compute and return a value of its declared type
(FN-25). Draw from the random stream (INTR-31). Nothing observable
outside its own frame; locals are ephemeral (FN-50).

**May not** — mutate state variables (FN-30) *or read them* (FN-32);
reference a pi by name (FN-35); call ``sched()``, ``stop_r()``,
``stop_s()``, or ``halt()`` (FN-30); ``timeout`` or ``claim`` (AG-80,
CLAIM-40); spawn agents (AG-25); change resource capacity (RC-60).


Init block (``init``)
---------------------

Has the same powers as an agent body (INIT-30): spawn agents, call
functions, ``timeout``, ``claim``, ``release``, mutate state, set
capacity. It is a singleton (INIT-10), runs once per run at ``t = 0``
(INIT-20), and runs after state and resources are reset (INIT-40).


Exec section
------------

Statements — including ``run()``, ``sched()`` registrations, and
``:print`` — are valid only after ``exec`` (EXEC-05). Assignment at exec
level reaches exec locals and module globals only (EXEC-45).
``:randseed`` is valid only here, at most once (EXEC-35). ``:capacity``
is *not* valid here: resources exist only for the duration of a ``run()``
(RC-60).

After a ``run()`` completes, exec statements may read a pi's or state
variable's final value directly, in addition to the observation-based
intrinsics (EXEC-45, INTR-40).


Cross-cutting rules
-------------------

These are the rules that close the system — the ones that make the table
above exhaustive rather than indicative.

============  ==============================================================
STATE-20      State variables are mutable **only** inside ``event``,
              ``agent``, or ``init``.
STATE-30      State variables *are* the Markov state; pure pi models have
              none.
SCEN-10       Scenarios contain only assignments to module-level
              variables, applied once per run before period 1 (SCEN-05) —
              the only sanctioned way module-level values change between
              runs.
SCOPE-40      No shadowing, anywhere.
GUARD-50      Guard expressions are side-effect free by construction.
GRP-60        Group declarations apply to ``pi``, ``state``, ``resource``,
              and ``agent`` only — not ``event`` (which has no persistent
              state to multiply) and not ``fn``.
SCHED-20/30   ``stop_r`` only inside ``pi``/``event``/``agent``/``init``;
              ``stop_s`` only at model level.
MOD-40        Imported non-state symbols are read-only; imported state
              variables are mutable only from an ``event``/``agent`` in
              the importer (MOD-50); ``agent``, ``resource``, and ``init``
              are module-private (MOD-80).  *Reserved — modules are not
              implemented.*
============  ==============================================================


Why the constraints exist
-------------------------

A bug in a Monte Carlo model does not crash. It produces a plausible
wrong distribution — the worst failure mode, because randomized output
cannot be verified by inspection. Nearly every constraint above converts
some source of silent bias into a compile-time error, and
``eb-errors.rst`` shows each one surfacing as a specific, positioned
message.

**All causality is findable.** State changes happen only in
``event``/``agent``/``init`` bodies (STATE-20) — a small, syntactically
identifiable set of sites. Function calls, pi references, and guard
expressions can never move the simulation behind your back (FN-30,
PI-40, GUARD-50).

**Pis are spreadsheet rows with the race conditions outlawed.** Once per
period, in declaration order, immutable once produced (PI-10, PI-50).
Same-period references must point backward in declaration order
(HIST-60), so same-period cycles cannot be written; feedback must go
through explicit history, making every recurrence visible in the source.

**Function dependencies are visible at the call site.** A function that
could reach into simulation state would have dependencies invisible where
it is called. FN-32 and FN-35 make the answer to "what state does this
touch?" always "none".

**An agent body is the whole story of its process.** Suspension is
syntactically visible — ``timeout``, ``claim`` — and no ``fn`` can hide a
wait, because functions cannot suspend at all (FN-30, CLAIM-40). Note
that explicit ``release`` (CLAIM-15) reintroduces one bug class the older
block form excluded: a held unit can now be forgotten. CLAIM-70 bounds
the damage by reclaiming held units when an agent terminates, so a leak
costs capacity for the agent's remaining lifetime rather than for the
rest of the run.

**Reproducibility is a theorem, not a hope.** DET-10 — same code, inputs,
scenario, and seed give identical observable results — is only promisable
because of the constraint system: single-threaded atomic handlers (EV-10,
DET-20), FIFO ordering in both the event queue (TIME-30) and resource
wait queues (CLAIM-30), exact IEEE 754 time comparison (TIME-50), and no
mutation channel outside the sequenced units. EXEC-30's total per-run
reset makes runs independent, which is what licenses parallel run
execution (DET-30) with no change in results.
