Skip to content

Prevent same-second checkpoint ID collisions from overwriting snapshots #38

Open

Description

@4444J99

Defect

At exact default head 685e682ce7fdbca3d2dcf0eb2ba4a1946d0e241a, CheckpointManager.create_checkpoint() derives snapshot_id only to whole-second precision:

snapshot_id = f"SNAP_{now.strftime('%Y%m%d_%H%M%S')}"

Two checkpoints created during the same second therefore share one path. The second write replaces the first snapshot, while CHECKPOINT_REGISTRY.json retains two entries with the same snapshot_id. A lookup by the first checkpoint name then loads the second checkpoint's state.

This is reproducible data loss, not a metadata-only concern.

Exact evidence

Pinned source blobs:

  • rege/persistence/checkpoint.py: 7f087d59b730811845415f0a77191b210d6fb5b8
  • rege/persistence/archive.py: afcab8688831f7fe760bb5cfc04aeee3bd1dba1b
  • rege/core/models.py: bd8666f9c4486f570706bf8323a529f4afa4d3f9
  • rege/core/exceptions.py: a2a06ef26d677171c24f3162bf77c0066f9e1d83

A deterministic isolated-source reproduction fixed datetime.now() to the same instant for two calls and confirmed:

  • both returned the same snapshot ID;
  • only one snapshot file remained;
  • the registry contained two entries;
  • loading the first name returned the second state.

A negative control advanced the clock by one second and preserved two IDs, files, and states.

Scope

Allowed implementation paths:

  • rege/persistence/checkpoint.py
  • rege/tests/test_persistence.py or one narrowly named persistence regression test

Touch rege/persistence/archive.py only if the chosen atomicity fix requires it. Do not change workflows, release configuration, packaging, generated coverage records, source-material archives, or unrelated persistence formats.

Open PRs #15, #31, #33, #35, and #37 do not touch these checkpoint paths.

Acceptance

  1. Two checkpoints created at the identical wall-clock instant receive distinct stable IDs and distinct snapshot files.
  2. Each registry entry resolves to its own saved state; no stale-name aliasing or silent overwrite remains.
  3. Registry update and snapshot persistence fail closed: a failed write must not create an entry that points to missing or different data.
  4. Existing load, list, export, import, delete, latest-selection, and pruning behavior remains covered.
  5. Run and record:
    • python -m pytest -q rege/tests/test_persistence.py rege/tests/test_persistence_coverage.py
    • the new same-second collision regression independently
  6. Return exact head, commands, pass/fail counts, and changed paths. Do not weaken assertions or delete historical data to obtain green.

Delivery boundary

Implementation may be prepared as a draft only. This issue grants neither merge nor release authority. The repository's current release-drafter.yml writes release-draft state on pull-request open/synchronize, so dispatch must retain that side-effect gate and revalidate it before creating provider output.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions