Two specifications CONTRACT-01 owes the slices that cannot be built without them. seed-derivation-v1: the versioned derivation of independent per-agent seeds from a recorded master seed and stable agent ids, the exact hashed material, the lane rule that keeps a seed away from zero (the pinned kernel RNG is an xorshift, whose state must not be zero), the properties a composition may rely on, and the test vectors both languages reproduce. checkpoint-envelope-v1: the exact byte layout of the new envelope per state-media-v1 section 4, with the header, canonical-JSON manifest, 112-byte payload table entries, eight-byte aligned payloads and the digest footer; the manifest field set; what a reader enforces and in what order; and the durable commit sequence of section 6, whose commit point is the store manifest rename. FLYSIM01 is unchanged, refused at the magic, and stays separately readable. Both are specifications with fixtures, not the store: generations, rotation, the capture queue and the restore flow remain STATE-01.
101 lines
5.2 KiB
Markdown
101 lines
5.2 KiB
Markdown
# Seed derivation v1
|
|
|
|
Status: **draft 1**, 2026-09-22. Specified by CONTRACT-01 of the
|
|
[implementation guide](implementation.md), required by
|
|
[worker interfaces](workers-v1.md) section 2 before the real-agent slice. Reference
|
|
implementations: `services/flysim/crates/fly-session-types/src/seed.rs` and
|
|
`packages/session-types/src/seed.ts`; test vectors:
|
|
`services/flysim/crates/fly-session-types/fixtures/seed-vectors.json`.
|
|
|
|
## 1. What this is for
|
|
|
|
`Agent.Initialize` takes `seed`, a signed 32-bit integer, matching the current RNG input.
|
|
Workers-v1 section 2 requires that the coordinator derive independent per-agent seeds from
|
|
**its recorded master seed and stable agent IDs** under a versioned algorithm, and that the
|
|
algorithm be specified and tested before the real agent slice. This is that algorithm.
|
|
|
|
It is a reproducibility rule, not a secret: a run manifest records the master seed in the
|
|
clear, and anyone with the manifest can recompute every agent's seed. It is not a key
|
|
derivation function and must not be used as one.
|
|
|
|
`seed-derivation-v1` is part of composition identity. Changing any byte of it requires a new
|
|
identifier (`seed-derivation-v2`), because two runs that agree on every other identity but
|
|
disagree here are not the same experiment.
|
|
|
|
## 2. Inputs
|
|
|
|
| Input | Type | Source |
|
|
| --- | --- | --- |
|
|
| `masterSeed` | `U64` decimal string | Recorded once per run by the application/supervisor |
|
|
| `agentId` | `Id` | The configured agent identity, stable across restarts and epochs |
|
|
|
|
Both are the ipc-v1 section 2 scalars. An `agentId` that is not an `Id` is an error, not
|
|
something to normalize. The master seed is the whole 64-bit range: a 32-bit master seed would
|
|
be no wider than the seed it derives.
|
|
|
|
## 3. Derivation
|
|
|
|
```text
|
|
material = "flybrain/seed-derivation-v1" LF masterSeed LF agentId LF
|
|
digest = SHA-256(material)
|
|
lanes = digest read as eight big-endian uint32 values, in order
|
|
seed = the first nonzero lane, reinterpreted as a two's-complement int32
|
|
```
|
|
|
|
`LF` is one `0x0a` byte. `masterSeed` is its canonical decimal form: `"0"`, or no leading
|
|
zero. The prefix is a domain separator, so a digest from this algorithm can never collide with
|
|
one taken over some other pair of strings.
|
|
|
|
Zero lanes are skipped because the pinned kernel's RNG is an xorshift generator, whose state
|
|
must not be zero: a derivation that could hand out `0` would silently produce a stalled
|
|
generator. If every one of the eight lanes were zero, the material is rehashed with a counter
|
|
suffix (`material || "1" LF`, then `"2" LF`, then `"3" LF`) and the search repeats; no input
|
|
has ever needed it, and four rounds exhausted is an error rather than a fallback seed.
|
|
|
|
The seed is the *negative* number when the lane's high bit is set. That is deliberate: the
|
|
existing RNG input is a signed 32-bit integer, and half the range is negative.
|
|
|
|
## 4. Properties
|
|
|
|
- **Deterministic.** The seed is a function of the two recorded inputs and nothing else: not
|
|
of wall time, agent order, port assignment, worker process or thread count.
|
|
- **Independent per agent.** Distinct agent IDs give unrelated seeds; there is no arithmetic
|
|
relationship between `fly-a` and `fly-b` for a caller to exploit or accidentally rely on.
|
|
- **Stable across recovery.** Restore, episode reset and a new epoch do not re-derive a
|
|
different seed for the same agent ID under the same master seed. The seed is persisted as run
|
|
configuration and state, and the capture compatibility digest covers the resolved seed
|
|
(workers-v1 section 2), so a checkpoint cannot be installed into a differently seeded
|
|
instance.
|
|
- **Equal IDs give equal seeds.** That is the only way to get identical seeds, and workers-v1
|
|
allows identical seeds only when an experiment declares them. A composition therefore
|
|
refuses a repeated agent ID rather than quietly sharing a seed between two agents.
|
|
|
|
Non-properties, stated so nobody assumes them: this is not uniform over the int32 range beyond
|
|
what SHA-256 gives, it is not a stream (one seed per agent per run, not per step), and it says
|
|
nothing about how a model consumes its seed.
|
|
|
|
## 5. Test vectors
|
|
|
|
`fixtures/seed-vectors.json` carries the full table: five master seeds (`0`, `1`, `42`, `2^63`
|
|
and the `U64` maximum) across four agent IDs, each with the exact material string, its SHA-256
|
|
and the derived seed, plus one four-agent composition and the inputs that must be refused.
|
|
Both implementations reproduce every row, and each records the material as well as the seed so
|
|
a third implementation can find where it diverges.
|
|
|
|
The first two rows:
|
|
|
|
| masterSeed | agentId | material | seed |
|
|
| --- | --- | --- | ---: |
|
|
| `0` | `fly-a` | `flybrain/seed-derivation-v1\n0\nfly-a\n` | 1828176714 |
|
|
| `0` | `fly-b` | `flybrain/seed-derivation-v1\n0\nfly-b\n` | 1218785088 |
|
|
|
|
Refused: an agent ID that is not an `Id` (uppercase, empty, over 64 characters), a master seed
|
|
that is not a canonical `U64`, and a composition with a repeated agent ID.
|
|
|
|
## 6. Out of scope
|
|
|
|
Choosing the master seed, recording it in the run manifest, and the hand-selected explicit
|
|
seeds that workers-v1 allows for the first synthetic composition. This document defines only
|
|
the derivation. A profile that needs several independent streams inside one agent derives them
|
|
from the agent's own seed under its own documented rule; that is a profile concern, not a
|
|
session one.
|