Skip to content

get_prior_attempts: an id-less supersede flips every earlier reverted attempt to "reopened" #30

Description

@masondelan

What happens

In get_prior_attempts, a reverted attempt's outcome flips to reopened when a later supersede event matches its closing removal. The match logic (selvedge/storage.py ~1487–1499) accepts either:

  1. an explicit link — s["supersedes"] == closing["id"], or
  2. the timestamp fallback — not s["supersedes"] and ts(s) >= ts(closing).

Branch 2 is evaluated per closing removal, so a single id-less supersede event matches every earlier removal on the path, not just the decision it actually re-opens. The comment directly above the fallback names this exact hazard for explicit links ("otherwise a supersede of one revert would flip every earlier, unrelated revert on the same path to 'reopened'") — but the id-less fallback re-introduces it.

Repro sketch

  1. Entity users.auth_token: log addremove (decision A, reverted).
  2. Same entity later: log addremove (decision B, independent, also reverted).
  3. Hand-log a supersede with no id link (selvedge log users.auth_token supersede with empty --supersedes, or log_change over MCP without an id).
  4. get_prior_attempts users.auth_token reports both A and B as reopened. Only B (the one the supersede follows) should flip; A's revert still stands.

Expected

An id-less supersede should re-open at most the single removal it auto-links to (the most recent one at write time — matching log_supersede's own auto-link rule), leaving earlier, unrelated reverts reverted.

Related observation (same code area)

log_supersede's id-less auto-link query matches the entity and its prefix descendants (entity_path LIKE 'canonical.%'), so selvedge supersede users can silently bind the most recent removal on a child such as users.email, writing a supersede on the parent path that points at a child's event. Worth deciding whether cross-path auto-linking is ever intended.

Found during the v0.3.10 release review; present in v0.3.10.

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