Summary
nondeterminismPolicy says a host declares some nondeterministic sources instead of suppressing them, and replay.md requires every declared source to be recorded so a fork replays it. But the record is a bare boolean, declared, and names no source, so the rule binds nothing a client can check. This RFC adds an optional sources[] facet from a small closed vocabulary (clock, random, id, env, plus x-*), defines the observable (a replay fork reproduces each declared source's value), and adds a fixture-gated witness. It also states that declared: false and envelopeContracts.advertised: false are not v2 states: under capabilities.md §2, a host that does not offer a family omits it. The two required booleans cannot leave inside v2 (§0a), so their removal is recorded for 3.0.
Motivation
The rule has no object. replay.md §Declared nondeterminism: "A host advertising nondeterminismPolicy.declared MUST record every declared source in the run's event log at the point it is read, so a fork replays the recorded value rather than re-drawing it." The record is { declared: boolean }. Which sources? None are named, so no client can tell what to expect from a fork, and no suite can test it. RFC 0085 chose the bare flag deliberately for v1.x (its UQ2) and named "a structured per-source policy" as the refinement; this is that refinement.
Two booleans bring back supported: false. v2 removed supported because presence is the claim (capabilities.md §2). nondeterminismPolicy.declared and envelopeContracts.advertised are required booleans, so a host can advertise either family with false: present, yet denying the behaviour. Nothing conforming needs that state.
Why they stay for now. overview.md §0a lets a 2.x minor retire only an optional surface, and forbids reshaping a surface in place. Both booleans are required, so removing them, or making them optional, waits for 3.0.
Proposal
§A The sources[] facet
nondeterminismPolicy gains an optional facet sources: an array of unique strings, each one of:
| Source | What the host draws |
|---|---|
clock | the current time, read by a node or by the host on a node's behalf |
random | random numbers or bytes |
id | identifiers the host mints during a run (not the run's own runId) |
env | host configuration or environment values read during a run |
x-<name> | a vendor source, named in the host's documentation |
LLM output and the results of side-effecting nodes are not sources here: replay.md already governs them through the invocation log and recorded outcomes.
§B The rule
A host advertising nondeterminismPolicy with sources:
- MUST record each value it draws from a listed source during a run, at the point it is read;
- MUST, on a
replayfork, reproduce the recorded value rather than draw again.
A source the host neither lists nor suppresses remains a replay defect (replay.md, unchanged).
§C false is not a v2 state
capabilities.md §2 already says a host that does not support a family MUST omit it. Stated for these two families:
- a host that does not declare its nondeterminism MUST omit
nondeterminismPolicy, and MUST NOT advertisedeclared: false; - a host that does not enforce envelope contracts MUST omit
envelopeContracts, and MUST NOT advertiseadvertised: false.
The schemas keep both booleans required. A spec/v1/deprecations.json row records their removal at 3.0.
§D The witness fixture
conformance-nondeterminism is a host-installed fixture workflow. Its run's outputs carry one string per source the host lists, keyed by source name, each drawn during the run (for clock, an RFC 3339 time; for random and id, the drawn value; for env, a value of the host's choosing). A host lists the fixture in its advertised fixtures.
Falsifiability — one row per normative requirement
| Requirement | Observable — what an outside party sees | Who can cause the condition | Verdict |
|---|---|---|---|
§B a declared source replays its recorded value — openwop.requirement.0237.declared-source-replays | the fixture's run outputs, then a replay fork from sequence 0: each listed source's output is byte-equal | the suite, with the host-installed fixture | witnessable — executed-pass required on a host bundle |
| §B recorded at the point it is read | only through the fork equality above; the log entry itself is host-internal | — | unwitnessable beyond the fork — the row above is its witness |
§C false is not a v2 state — openwop.requirement.0237.no-false-advertisement | a discovery document advertising nondeterminismPolicy.declared: false or envelopeContracts.advertised: false | the suite, unaided | witnessable — executed-pass required on a host bundle (inapplicable when neither family is advertised) |
Compatibility
additive.
sourcesis optional, so a record without it validates unchanged. A host that does not list it keeps today's behaviour.- §C restates
capabilities.md§2 for two families and changes no schema. Census: no committed v2 host bundle advertises either family. - The fixture is opt-in. A host that does not install it records
inapplicablefor the §B leg. - The removal of the two booleans waits for 3.0 (
overview.md§0a predicate 4). conformance/src/lib/profiles.tsreadsnondeterminismPolicy.declaredfor a v1 profile predicate; v1 is frozen and unaffected.
Conformance
Existing: v2-nondeterminism-policy-advertisement and v2-envelope-contracts-advertisement validate the records against their schema seats.
New: v2-nondeterminism-sources, gated on nondeterminismPolicy.sources and the fixture. It runs conformance-nondeterminism, forks the run in replay mode from sequence 0, and requires each listed source's output to match. A scratch double that draws again on replay must fail it. The §C leg joins the two advertisement scenarios. The family's witness class moves from claims-check to witnessable-gated.
Alternatives considered
- Read the recorded value from the event log. It needs a new event type for a nondeterministic read and a rule for where it sits in the log. The fork equality observes the same obligation through surfaces that already exist.
- An open vocabulary. It cannot be tested: a suite cannot know what an unknown source should reproduce. The closed list covers what a host draws on its own;
x-*covers the rest without a promise of testing. - Make the booleans optional now. §0a forbids reshaping a v2 surface in place, and making a required field optional is such a reshape.
- Do nothing. The replay rule stays unfalsifiable, and two families keep a state v2 removed everywhere else.
Unresolved questions
None. Two were decided at Active (2026-10-06, steward):
envis listed: a run that branches on configuration must replay the branch it took.- Fork origin is sequence 0 only; a mid-run fork tests the same mechanism with more fixture coupling.
Implementation notes (non-normative)
- v2 reference host: a fixture node that draws
clock,randomandidinto its output and records them; on areplayfork it serves the recorded values. It then listssources: ["clock", "random", "id"]. - Sequencing: Draft → Active (prose, declaration facet, fixture contract, deprecation row, legs), then the reference host adopts it and cuts, then
Accepted.
Acceptance criteria
- [x]
Active: §A–§D merged;sourcesinspec/v2/declaration.json; the fixture inconformance/fixtures.md; the 3.0 deprecation row;CHANGELOG.md. - [x]
v2-nondeterminism-sourcesand the §C legs ship in suite 2.45.23, each failing on its sabotage against a double: a fork that draws again fails the replay leg, anddeclared: falsefails the §C leg. - [x]
Accepted: a certified major-2 bundle recordsopenwop.requirement.0237.declared-source-replaysandopenwop.requirement.0237.no-false-advertisementexecuted-pass. (2026-10-08: the v2 reference host's certified public cut on published suite 2.45.25, buildcommit:a8e6db64; bothexecuted-pass.)
References
spec/v2/core/replay.md§Declared nondeterminism;spec/v2/core/capabilities.md§2;spec/v2/core/overview.md§0a;spec/v2/core/events.md§Envelope contracts;COMPATIBILITY.md§2.4, §3a.- RFC 0085 (the
nondeterminismPolicyflag, its UQ2); RFC 0197 (v2 retirement). - Prior art: Temporal's deterministic side-effect recording (
workflow.SideEffect,workflow.Now); Restate's journaled side effects. - Registers:
gaps,risks.