Skip to content

fix(persistence): enforce membership weight and share budgets - #615

Draft
seonghobae wants to merge 34 commits into
feat/analysis-run-persistencefrom
fix/membership-persistence-weight-bounds-614
Draft

seonghobae wants to merge 34 commits into
feat/analysis-run-persistencefrom
fix/membership-persistence-weight-bounds-614

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Scope

Dependent persistence repair for the Membership owner contract. This PR remains intentionally stacked on #287 (feat/analysis-run-persistence) so it inherits the active persistence writer's 0008_analysis_run_persistence migration. 0009 closes the row-local (0, 1] mismatch; unreleased successor 0010 owns exact represented-binary64 same-role composition, temporal uncertainty, duplicate-edge, upgrade-validation, and concurrent-writer parity. Historical 0006/0009 are not rewritten.

Current base authority: #287 exact 9fd492a3b3873d03460c1393308d3c8d1ca73f83.
Current exact head: 10b07fcdf47f867e3c7b78ba1b8e8a2a668cc4b3.
Base→head is ordinary-forward; Draft remains required.

Row-local parity (#614)

Membership owner #605 defines affiliation shares in (0,1], while the predecessor persistence boundary rejected only non-finite/<=0 values. MembershipAssignmentRecord and PostgreSQL 0006 therefore admitted finite values above one. 0009_membership_weight_unit_interval installs CHECK (membership_weight > 0 AND membership_weight <= 1) NOT VALID first, then validates it separately so new writes fail closed immediately without doing the historical-row scan under the initial stronger DDL lock.

Lineage: RED f8b161d571f0d48be7697ab8668ecd4f7da17025 / catalog RED a3d74094bda7d15b20d25a98709e45f7ab354259 → Rust repair e1a2414518543afe76affab15e416f54653876a3 → forward/rollback 0009 7eacf14e57cc2f16a817f7ccb4d4a2ee7aa682dc / b431eb2a9f7747d983a8ef0e444ba38af27b3c3a → embedded ordering 964fa844c74a5a864024338c4fc08cc27423c334 → operational RED c017cf9b3456def4dfc2f961db1047d6b80136c5NOT VALID/VALIDATE repair b570f69742a256cbe8c38b944a6cd70bb6a8eab3.

Exact same-role composition (#616/#612)

0010 canonicalizes persisted NUMERIC shares through PostgreSQL double precision, extracts the IEEE-754 binary64 payload, and compares exact scaled integer numerators. Decimal-only SUM, ordinary floating accumulation, tolerance, clamp, and normalization are not used. The guard row serializes one (tenant, observed unit, role) lane so concurrent first writers cannot both observe an empty lane.

Original lineage: RED 6668a161a312f546c58b5a5823e27b19bb3f29e6 → first exact-binary64/guard repair 56c9a89c2adc98b673966bbb815b780fc65c2410 → rollback db3607885378f02a8ba472f7199fb75e21ad4d7d → live contract 4cf24a02c0e24fff3a0fe883345dd92bee3edabd → embedded catalog 7e48075bde8c95bc6cc62ec8615fcb8145330460 → canonical live path 49f1575528548005ba8967df10f3606eb46dacc9.

Temporal uncertainty (#617/#618)

0006 admits non-empty tstzrange uncertainty windows with unbounded sides and explicit endpoint inclusivity. Scalar lower()/upper() comparison was unsound: unbounded sides became SQL NULL, and equal timestamps lost open/closed semantics.

#617 RED 4ad3a3cef1ef77972d71cb93b7b7627405c901b1 and #618 RED bed68e3aceb49170a5a984622940159f22c26456 led to range-native possible-activity envelopes; #618 repair ebd816eb9d8153d54905273aefe05ae7846fc767 preserves unbounded/open/closed semantics without timestamp nudging. def43f135a75d6d20ed51ee918b902ede0ddf8cf was compile/API-only doctoring.

Pointwise temporal composition (#620)

Pairwise overlap with the candidate is not enough to aggregate weights. Two existing spells can each overlap a spanning candidate at different event times without ever co-occurring. Old 0010 could therefore reject a valid history such as early 0.6, late 0.6, spanning 0.4 even though every pointwise total is <= 1.0.

  • behavioral RED 6eaa5dc2caf601b65112996a436f37e455a2916e adds the disjoint-existing/spanning-candidate positive case and a genuine triple-overlap negative control;
  • causal repair aa43c6520f71744504c703bd853a97a53571341f replaces pairwise candidate-overlap SUM with an event-state sweep over exact boundary points, right-open states, and the lower-unbounded state while retaining exact binary64 numerators;
  • rollback helper repair 75d6f65de7c4dd3bcc8f67d3d747a69a7a033e95.

Pre-existing state validation (#619)

A future-write trigger alone cannot make predecessor data reconstructable. 0006/0009 can contain positive NUMERIC values that underflow to binary64 zero, ordinary same-role overruns, the #612 boundary overrun, or duplicate temporal edges.

44e310839bcc24bcefc503db4777b899614120de adds a live predecessor→successor upgrade contract. 0010 validates historical rows with the same binary64, pointwise temporal, and duplicate-edge semantics used by admission. Invalid state fails the migration rather than being normalized.

Duplicate temporal-edge parity (#621)

Membership owner #605 rejects overlapping (member, group, role) duplicate edges even when their weights sum to unity or less. Persistence previously enforced only the aggregate share budget.

  • RED 5923151335d440b33df3aab17961451e51c53124 proves overlapping same-target/same-role duplicates must fail while strict leave/re-entry, different targets, and different roles remain valid;
  • causal repair 98abb27c7c5a29f4ec4ea7502375987cdea84035 adds same-target/same-role range-native duplicate detection to both live admission and legacy validation and includes target-only UPDATEs in the trigger surface;
  • rollback 81b400b5bb8a869b9622509f90b35f26b85af7e6 and fixture doctoring f2de8acfe9e52ade01b5183c51407f388400b88e preserve non-empty uncertainty windows;
  • target-only UPDATE acceptance 1c2328259cfb8f507b9b7809ebe565dde809a412 verifies changing only the target cannot bypass duplicate-edge admission and that rejection is atomic.

Retry-safe cutover (#622)

Historical validation originally ran before the trigger. Because the repository applies migration statements sequentially rather than as one giant transaction, a predecessor writer could commit invalid state between the scan and trigger installation. Holding a long explicit table lock across the scientific scan would be the wrong operational trade-off.

  • repair 84e5859a34a7864f06d74794bac2f6180ac05e97 installs retry-safe guard/RLS/function/trigger admission before the historical scan;
  • live upgrade contract f471211abbce2503d51c503533d5d197f6c0b09e proves a failed historical validation leaves future writes protected and explicit data-owner remediation can be followed by an idempotent retry;
  • second RED 54bf6128333b6698c2926ed20b9945aa917ecf43 detects a remaining retry window from DROP TRIGGER; CREATE TRIGGER;
  • current causal repair 10b07fcdf47f867e3c7b78ba1b8e8a2a668cc4b3 uses PostgreSQL 18 CREATE OR REPLACE TRIGGER, so retry replaces the active gate without a committed drop/create interval.

Current gate

This is not GREEN. Fresh exact-head Rust Foundation run 35463797907 on 10b07fcd... is queued; predecessor receipts do not transfer. Hosted PostgreSQL 18 must prove exact-binary64 arithmetic, pointwise temporal composition, bounded/unbounded and open/closed uncertainty, duplicate-edge refusal including target-only UPDATE, predecessor upgrade validation, retry-safe/no-gap cutover, embedded rollback, and concurrent first-writer behavior.

#287 itself is still not GREEN. The active foundation prerequisite remains #538 f335624...; do not duplicate its dispatch/dependency-policy repairs here. #538 must land normally, then #287 and this branch must be reconciled ordinary-forward and reacquire exact-head checks.

Acceptance

No self-approval, force push, destructive rebase, blind rerun, historical released-migration rewrite, decimal-only aggregate, timestamp epsilon, tolerance, clamp, normalization, source copy, or cross-service SQL is authorized.

Refs #604, #605, #610, #612, #613, #614, #616, #617, #618, #619, #620, #621, #622.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae seonghobae changed the title fix(persistence): enforce membership weight unit interval fix(persistence): enforce membership weight and share budgets Sep 19, 2026
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.

1 participant