Repository navigation
Description
Finding
After #617 closes the unbounded-range NULL bypass, the #616 storage trigger still compares only scalar lower()/upper() timestamps with <=. That loses the inclusivity metadata carried by PostgreSQL tstzrange uncertainty windows.
0006_typed_membership_assignment permits non-empty ranges with inclusive or exclusive endpoints. The share-budget rule is intentionally conservative only when two memberships can overlap. Two uncertainty envelopes that merely meet at timestamp t cannot overlap when at least one relevant endpoint excludes t, but the current scalar predicate treats equality as overlap unconditionally.
Example: an existing membership has valid_to_window = '[2026-04-10,2026-04-11)' and a candidate has valid_from_window = '[2026-04-11,2026-04-11]'. The existing end can never reach 2026-04-11, so the spells are disjoint. The current upper(existing.valid_to_window) == lower(candidate.valid_from_window) comparison nevertheless rejects a second full share.
This is a buyer/scientific correctness issue in the opposite direction from #617: fail-closed storage must not silently collapse valid time-varying disjoint spells into a false overlap.
Required RED
On #615 or its verified successor, add canonical embedded PostgreSQL 18 contracts proving:
- excluded existing upper endpoint + included candidate lower endpoint at the same timestamp is disjoint and remains admissible;
- included existing upper endpoint + included candidate lower endpoint at the same timestamp is a real possible overlap and rejects an over-budget write;
- included existing upper endpoint + excluded candidate lower endpoint at the same timestamp is disjoint and remains admissible;
- fix(persistence): close unbounded temporal-window bypass in membership share budget #617 upper/lower-unbounded cases remain fail closed when overlap is possible.
Causal repair boundary
Preserve PostgreSQL range semantics directly. Prefer constructing the possible-activity envelope from the earliest admissible start and latest admissible end while retaining endpoint inclusivity, then use range-overlap semantics (&&) rather than timestamp nudging, epsilon/tolerance, or ad hoc microsecond arithmetic. Open-ended and unbounded uncertainty must remain supported.
Do not change the exact binary64 share arithmetic, tenant/observed-unit/role guard serialization, row-local (0,1] constraint, or historical migrations.
Acceptance
Keep open until public RED -> minimal owner-consistent repair, exact-head live PostgreSQL evidence, coverage/security/review, prerequisite landing/restack, and downstream #604 recovery are complete.
Activity
seonghobae commentedon Sep 19, 2026
#615 now carries the endpoint-semantics repair ordinary-forward.
bed68e3aceb49170a5a984622940159f22c26456ebd816eb9d8153d54905273aefe05ae7846fc767tstzrangefrom earliest admissible start to latest admissible end;&&instead of scalar<=, timestamp nudging, or tolerance;Fresh exact-head hosted evidence is still pending, so #618 remains open and no GREEN/merge claim is made.
seonghobae commentedon Sep 19, 2026
Current surviving #615 head is now
def43f135a75d6d20ed51ee918b902ede0ddf8cf. The only delta after causal repairebd816eb...is compile/API doctoring: the new live-test helper originally declaredResult<u64, PersistenceError>while canonicalSqlSession::executereturnsResult<(), PersistenceError>;def43f13...aligns the test helper to that existing API without changing production semantics. Fresh exact-head Rust Foundation35461497979is queued with all four jobs still pre-checkout, so no GREEN claim or blind rerun.