Skip to content

fix: derive scene seed from instance_id, not sorted index#24

Merged
jeqcho merged 3 commits into
robocurve:mainfrom
Kayvan-Zahiri:fix/seed-decorrelate-instances
Jul 14, 2026
Merged

fix: derive scene seed from instance_id, not sorted index#24
jeqcho merged 3 commits into
robocurve:mainfrom
Kayvan-Zahiri:fix/seed-decorrelate-instances

Conversation

@Kayvan-Zahiri

Copy link
Copy Markdown
Contributor

Closes #3.

Scene seeds now come from a hash of instance_id instead of the instance's sorted
position. derive_seed hashes only (eval_seed, scene_seed, epoch), so passing
index meant task identity never entered the seed and instance i of every task
drew from the same PCG64 stream. Distinct scene seeds go from 5 to 50, and
cross-task duplicate draws go from 21% of all continuous setup values to zero.

Results before and after this change are not comparable. Every realized scene is
different. That is the point of the change and it is stated at the top of the
changelog.

What's here

  • tasks.py: seed from crc32(instance_id). Used crc32 rather than hash() on
    purpose, since hash() is randomized per process by PYTHONHASHSEED and would
    have made scenes non-reproducible across runs. No collisions across the 50 ids.
  • test_canonical_instruction_matches_epoch_zero: reads scene.init_seed instead of
    recomputing from index, per your note, so it pins the invariant rather than a
    seeding scheme.
  • CHANGELOG.md: new file, with the non-comparability warning and the numbers that
    justified the break.

Two things to check me on

Task versions. I bumped all ten (TaskSpec.version: nine 1 -> 2, scoop_pasta
2 -> 3). This is beyond what you asked for. Reasoning: every Scene stamps
spec.version into its metadata, so leaving them alone ships two different scene
sets under identical version strings, which is the silent non-comparability the change
exists to prevent. A changelog note is prose; that field is what a consumer can check.
Happy to drop it if you disagree.

Package version. pyproject single-sources it from git tags via hatch-vcs
("nothing is committed to main"), so the minor bump to v0.4.0 has to be a tag cut on
your side. Nothing in this PR touches it.

Gates

ruff check clean, ruff format --check clean, mypy clean, 307/307 pytest with the
100% coverage gate met.

The repro and impact scripts are in #3. Happy to add them under scripts/ if you want
them in-tree.

Kayvan-Zahiri and others added 3 commits July 13, 2026 17:10
tasks.py seeded each Scene with its sorted position, and derive_seed hashes
only (eval_seed, scene_seed, epoch), so task identity never entered the seed.
Instance i of every task therefore drew from a byte-identical PCG64 stream:
21% of the suite's continuous setup draws were exact cross-task duplicates.

Seed from instance_id (unique and task-qualified) instead. Scene seeds go from
5 distinct to 50; cross-task duplicate draws go to zero.

This CHANGES EVERY REALIZED SCENE and therefore every published number. Landing
it needs a version bump and a changelog note that results are not comparable
across the change.

test_canonical_instruction_matches_epoch_zero re-derived the seed from index
rather than reading scene.init_seed, so it pinned the seeding scheme instead of
the invariant it meant to check. It now reads the seed off the Scene.
Every Scene stamps spec.version into its metadata, so leaving the versions
alone would ship two different scene sets under identical version strings.
That is the silent non-comparability this change exists to prevent, so the
nine tasks on version=1 go to 2 and scoop_pasta goes 2 -> 3.

The changelog states plainly that results before and after this change are
not comparable, and records the measurement that justified the break.

Note: the PACKAGE version is single-sourced from git tags via hatch-vcs
('nothing is committed to main'), so the minor bump to v0.4.0 has to be a
tag cut on your side rather than something this PR can carry.
@jeqcho
jeqcho merged commit 6144319 into robocurve:main Jul 14, 2026
9 checks passed
jeqcho added a commit that referenced this pull request Jul 14, 2026
…25)

Post-merge cleanups from the #24 review:

- test_sim_blueprint's sweep paired scenes with derive_seed(0, index, epoch),
  seeds that no longer occur at runtime now that init_seed is crc32-derived.
  Sweep with derive_seed(0, scene.init_seed, epoch) instead. Pinned tests keep
  their fixed seed literals (they check blueprint/realization consistency at a
  seed, any seed); the one comment claiming those literals were real epochs is
  reworded.

- TaskSpec.version's default was bumped to "2" in #24, which would mint future
  new tasks at version 2. Restore the default to "1" and set version="2"
  explicitly on the nine existing tasks (scoop_pasta already pins "3").
  Realized version stamps are unchanged.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Seed correlation: instance i shares identical derived seed streams across all 10 tasks

2 participants