Skip to content

fix(persistence): close unbounded temporal-window bypass in membership share budget #617

Description

@seonghobae

Finding

The #616 persistence trigger currently reduces temporal uncertainty to two envelope instants:

  • candidate_start := lower(NEW.valid_from_window)
  • candidate_end := upper(NEW.valid_to_window) when valid_to_window is non-null

and performs the same lower/upper calls for existing rows.

PostgreSQL range accessors return NULL for an unbounded side. Migration 0006_typed_membership_assignment requires non-empty tstzrange values but does not require finite bounds, so valid direct-SQL rows such as '(,2026-01-01]'::tstzrange or '[2026-01-10,)'::tstzrange are admitted by the schema. In the #616 trigger, those NULL bounds flow into <= predicates and evaluate to unknown/false, so an overlapping same-role share can be omitted from the aggregate. That can persist a state whose owner-equivalent known share exceeds unity.

This is not an application-helper-only concern: #616 explicitly owns direct PostgreSQL parity and must fail closed for every schema-admitted temporal window.

Required RED

On #615 or its verified successor, add PostgreSQL 18 direct-write contracts proving at least:

  • an existing same-role membership with an upper-unbounded valid_to_window participates in the budget and prevents a later overlapping overrun;
  • a candidate with a lower-unbounded valid_from_window participates in the budget and cannot bypass an existing share;
  • rejected writes leave the persisted row set unchanged;
  • ordinary finite disjoint spells remain admissible.

The RED must exercise the canonical embedded migration path, not a test-only trigger definition.

Causal repair boundary

Treat schema-admitted unbounded start/end uncertainty as -infinity / infinity only for the conservative overlap envelope used by the storage admission predicate. Do not rewrite historical migrations, narrow 0006 ad hoc, or weaken the #616 binary64/share-budget contract. Preserve tenant/observed-unit/role serialization and exact binary64 numerator arithmetic.

If endpoint inclusivity requires separate refinement, keep it explicit; do not hide it behind tolerance or timestamp nudging.

Acceptance

Keep open until the surviving persistence vehicle has public RED -> minimal forward repair, exact-head live PostgreSQL evidence, owned coverage/security gates, qualifying review, normal prerequisite landing/restack, and downstream #604 recovery.

Refs #604, #615, #616.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions