Skip to content

persistence(membership): enforce unit-interval weight parity with Membership owner #614

Open

Description

@seonghobae

Persistence contract finding

membership_core::MembershipWeight on #605 owns affiliation-share semantics and rejects weights above one. TEPP's persistence boundary does not yet preserve that invariant:

  • MembershipAssignmentRecord::validate rejects non-finite and <= 0, but accepts finite > 1;
  • migration 0006_typed_membership_assignment has CHECK (membership_weight > 0) but no upper bound, so direct SQL can persist 1.25.

Zero is already correctly rejected in persistence and PostgreSQL; #613 aligns the Membership VO to that existing positive-domain contract. This issue covers the distinct upper-bound mismatch.

Single-writer / migration boundary

Do not rewrite historical migration 0006. PR #287 is the active persistence lineage and reserves 0008_analysis_run_persistence. The repair must therefore be an ordinary-forward dependent persistence vehicle based on #287's exact head, allocating 0009 only after inheriting #287's 0008 migration.

Required RED

The dependent persistence vehicle must prove before repair that:

  1. MembershipAssignmentRecord rejects 1.0 + f64::EPSILON and other finite >1 values;
  2. the database refuses a direct membership_assignment insert above one after the full embedded migration catalog is applied;
  3. positive sub-unity values and exact 1.0 remain valid;
  4. rollback removes only the new bound and restores the predecessor schema without rewriting earlier migration history.

Repair boundary

  • tighten the Rust record validator to finite (0,1];
  • add forward migration 0009_membership_weight_unit_interval with a named multiword constraint membership_assignment_weight_unit_interval CHECK (membership_weight > 0 AND membership_weight <= 1) (or equivalent fail-closed form);
  • update the embedded migration catalog in sequence after feat(persistence): persist idempotent analysis runs #287's 0008;
  • retain RLS, append-only, temporal, tenant and live PostgreSQL contracts;
  • no tolerance, clamp or normalization.

Acceptance

Exact-head persistence tests, live PostgreSQL 18.x migration/direct-write/rollback evidence, rustdoc/line+branch+edge coverage, security/CodeQL/Semgrep, and qualifying independent review. Keep this issue open through both #287 prerequisite landing and the dependent persistence PR's protected-main landing.

Activity

  1. seonghobae commented on Sep 19, 2026

    @seonghobae
    ContributorAuthor

    Dependent persistence vehicle is now Draft PR #615, stacked on #287 exact 9fd492a3b3873d03460c1393308d3c8d1ca73f83 with current head 1d199cc77b1b270fde64b1a0845aba19d0e937a7 (ahead 7 / behind 0). REDs: f8b161d571f0d48be7697ab8668ecd4f7da17025 (record >1 refusal) and a3d74094bda7d15b20d25a98709e45f7ab354259 (embedded catalog bound/rollback). Repairs: e1a2414518543afe76affab15e416f54653876a3 record validator (0,1]; 7eacf14e.../b431eb2a... forward+rollback 0009; 964fa844c74a5a864024338c4fc08cc27423c334 catalog sequencing after #287's 0008; 1d199cc77b1b270fde64b1a0845aba19d0e937a7 live PostgreSQL proof for 0.5/1 acceptance, direct 1.25 rejection, named constraint presence, and mutation atomicity. Keep #614 open through #287 landing, exact-head hosted acceptance/review, ordinary restack, and protected-main landing of #615.

  2. seonghobae commented on Sep 19, 2026

    @seonghobae
    ContributorAuthor

    #615 has advanced ordinary-forward to exact 2707865b407ddb1067b2a2da56161237426d0ad9 (base #287 9fd492a3..., ahead 10 / behind 0). Additional operational RED c017cf9b3456def4dfc2f961db1047d6b80136c5 rejected a plain ADD CHECK because it would combine catalog DDL with an existing-row scan under the stronger lock. Repair b570f69742a256cbe8c38b944a6cd70bb6a8eab3 now installs membership_assignment_weight_unit_interval ... NOT VALID first, then executes VALIDATE CONSTRAINT separately; new writes are constrained immediately while the existing-row scan runs under PostgreSQL's weaker validation lock. 2707865b... extends the live PostgreSQL proof to require pg_constraint.convalidated = true. Fresh exact-head Rust Foundation run 35458144110 is queued with all four jobs pre-checkout/unassigned; no predecessor evidence is transferred and no blind rerun is warranted.

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