Skip to content

test(people): prove Assignment Employment/Position conflict serialization with real PostgreSQL interleavings #272

Open

Description

@seonghobae

Current gap

This issue remains the real-PostgreSQL concurrency acceptance owner for Assignment. Its original causal model is now stale and must be repaired rather than closed.

On 2026-09-07, #271 correctly invalidated a redundant Assignment employment_record lock because every valid Assignment then crossed the current candidate_worker_conversion_record FOR UPDATE boundary first. #271 explicitly left a future condition: if an Employment lifecycle path can affect Assignment eligibility without crossing that converted-worker boundary, a new executable concurrency contract is required before changing the root lock.

That condition now exists on active People owner #64:

  • generic Employment creation no longer treats recruiting conversion provenance as mutation authority and serializes on the stable Person root;
  • candidate_worker_conversion_record is hire-origin provenance bound to a particular resulting Employment, not generic staffing authority;
  • migration 0017_assignment_employment_separation_serialization.sql already makes Employment the shared Assignment/separation conflict root and re-reads Employment coverage after the lock wait;
  • current Assignment production repair removes generic _CONVERSION_SQL authority and locks the named tenant-local Employment before reading Employment versions, then locks Position before reading the allocation portfolio.

Therefore the canonical conflict domains are now Employment aggregate + Position seat, not converted-worker provenance + Position.

Required executable acceptance

Use two independent real PostgreSQL connections against the repository migration stack and the production PostgresPeopleMutationPort. Synthetic structural rows are permitted only as transaction/lock mechanism fixtures.

  1. Same Employment/Person, different Positions, different idempotency keys: writer B must be observed in pg_stat_activity/pg_blocking_pids(...) waiting on writer A's Employment row conflict boundary. After A commits 0.7500, B must take a fresh READ COMMITTED Employment/Assignment snapshot and fail because the Employment allocation would exceed 1.0000.
  2. Different Employments/People, same Position: writer B must wait on the Position root and fail after A commits because visible seat allocation would exceed 1.0000.
  3. A Person/Employment created without any candidate-worker conversion must still be assignable when Employment/Position truth, authorization, confirmation, and evidence are otherwise valid. Historical recruiting conversion must not be needed to make the concurrency test pass.
  4. Assignment vs Employment separation must use the same Employment root and preserve both commit orders already owned by migration 0017 / its PostgreSQL contract.
  5. Assert accepted/rejected results, final Assignment rows, audit/outbox/idempotency atomicity, exact blocker identity, client/server cleanup, and no durable loser residue.

Correctness ordering must come from PostgreSQL-visible locks/barriers, not timing-only sleeps. Keep READ COMMITTED. Do not use broad table locks, retry-as-correctness loops, sample reduction, synthetic verdicts, or direct provider/gate bypasses.

Cleanup acceptance

Failure-path cleanup remains part of this issue. Every started writer must quiesce before PostgreSQL teardown. Bounded join expiry is not proof of cleanup: terminate only the verified owned backend using captured PID + internal application_name, require exact one-session successful termination, then re-join with a bounded deadline and fail closed if the writer remains live.

Existing evidence to preserve

The historical libpq two-session harness and cleanup repair sequence remains valid evidence about the harness mechanics; do not discard it. However names/assertions that still describe the Employment conflict as converted_worker are now stale and must be ordinary-forward currentized to Employment-root semantics. Predecessor GREEN cannot be transferred to a head after this causal change.

Canonical source owner is #64 (or its normally integrated verified successor). Canonical PostgreSQL discovery/execution owner is #311 after its prerequisite stack integrates. Do not copy mutable #64 source into another writer or add another feature-local quality workflow.

Completion

Keep this issue open until an exact-head hosted PostgreSQL run proves both current conflict domains on the repaired production adapter, including the no-candidate-conversion Assignment case and failure cleanup, and the result is adopted through the protected owner path. Commercial/scientific claims still require right-cleared real data; synthetic concurrency fixtures are mechanism evidence only.

Activity

  1. seonghobae commented on Sep 7, 2026

    @seonghobae
    ContributorAuthor

    #272 executable acceptance remains owned by #64. Fresh review of the first implementation head fb28b01915cd8bf233260cfa3c4b562879f9ff7b found two harness-only defects: an unused pytest import and _run_writer catching BaseException. Both are valid findings because the module uses no pytest API directly, and a test worker must not swallow interpreter control-flow exceptions.

    Ordinary forward 015ba7ce98da5a3100194ec9e3389df8b0cedb5e removes the unused import, narrows captured worker failures and _WriterOutcome.error to Exception, and changes no production adapter/locking semantics. Exact compare from the implementation head is 1 commit ahead / 0 behind and only this acceptance file changes (+2/-4). Both inline review threads were replied to and resolved.

    Fresh exact-head workflows are Foundation 34106650398, Security 34106650296, SAST 34106650302, and CodeQL 34106650323; all are currently non-terminal. Keep #272 open until Foundation executes the real two-session PostgreSQL interleavings on this exact head and the required hosted evidence is terminal.

  2. seonghobae commented on Sep 7, 2026

    @seonghobae
    ContributorAuthor

    Fresh acceptance audit found a separate evidence-classification defect after the harness review repair. The current PostgreSQL interleaving test constructs deterministic UUIDs and inserts its own Person/Employment/Position/Candidate/Decision rows; no repository provenance proving those rows are real/right-cleared data exists. That means the test is valid database serialization integration evidence, but it does not yet satisfy this issue's own real/right-cleared acceptance criterion and must not be represented as buyer/scientific reality evidence.

    Ordinary forward 1ea5cb1dcc887cf5b83fa85f926228e146f09d3f makes the source truthful without weakening the test: the module now states that its structural records are deterministic synthetic fixtures, the seed helper no longer calls them right-cleared, and the seeded decision reason is classified as an integration fixture. No lock, SQL path, authorization, idempotency, audit/outbox, barrier, assertion, allocation sample, or production source changed. Exact compare from 015ba7ce... is 1 commit ahead / 0 behind and only this test file changes (+7/-3).

    #272 therefore remains open with two distinct evidence layers: (1) run the current two-session PostgreSQL integration contract on the exact head to prove lock/wait/visibility/atomicity mechanics; (2) before calling the requirement complete, add provenance-backed real/right-cleared Assignment data or an approved right-cleared fixture source through the canonical owner path and rerun the relevant behavioral acceptance. Do not fabricate provenance or relabel synthetic rows as right-cleared.

    Fresh workflows for 1ea5cb1d... are Foundation 34107536416, Security 34107536442, SAST 34107536438, and CodeQL 34107536387; all are currently non-terminal.

  3. seonghobae commented on Sep 7, 2026

    @seonghobae
    ContributorAuthor

    Current exact-head gate classification for the implemented mechanism evidence: #64 818950960c4fe0a4e7b026bd5499fe9cca1002be has Foundation 34131641452 terminal SUCCESS and SAST 34131642172 terminal SUCCESS. Security 34131642055 is terminal FAILURE only at central Dependency Review support (.github#810); CodeQL 34131641597 remains queued. The Foundation success is the relevant executable evidence for this issue: it ran the full People test tree under ORGMETRA_POSTGRES_IMAGE, including both real two-session interleavings and forced post-lock cleanup/timeout paths. Therefore PostgreSQL mechanism/integration acceptance stays GREEN on this immutable head; no Security/CodeQL result is being reinterpreted as mechanism evidence. Commercial/scientific realism remains unresolved until a permitted provenance-backed real/right-cleared source exists.

  4. seonghobae commented on Sep 7, 2026

    @seonghobae
    ContributorAuthor

    Current exact-head evidence supersedes the issue body's stale non-terminal gate sentence. Canonical #64 remains 818950960c4fe0a4e7b026bd5499fe9cca1002be; Foundation 34131641452 is terminal SUCCESS and materially executes the People suite containing both real PostgreSQL concurrency modules, while SAST 34131642172 is terminal SUCCESS. Security 34131642055 is terminal FAILURE only at the shared Dependency Review support probe (.github#810), and CodeQL 34131641597 is terminal FAILURE in the central current-head verdict ordering/publication path (.github#1929). Those control-plane failures do not reclassify the PostgreSQL mechanism tier as RED.

    The mechanism/integration tier is therefore current-head hosted GREEN. Keep #272 open solely for the explicitly separate commercial/scientific realism tier until a provenance-backed, permitted real/right-cleared data source exists and is executed without weakening the synthetic-vs-real evidence distinction.

  5. seonghobae commented on Sep 12, 2026

    @seonghobae
    ContributorAuthor

    Fresh #64 domain review changes the interpretation of this issue's worker-serialization anchor, not the required concurrency outcome.

    The original acceptance correctly requires same Employment/Person Assignment writers to serialize before evaluating the allocation snapshot. However, candidate_worker_conversion_record is now verified to be recruiting-origin provenance: its protected composite FK points to an already-existing (Employment, Person) and the confirmed-hire path materializes it from a sealed selection decision. The generic People Employment route allocates a future Employment server-side. Therefore a candidate-conversion row cannot remain the canonical generic People authorization/concurrency anchor merely because it happened to provide a convenient row lock.

    Current #64 RED 20e29962311b17eb004412cd775dd5ada0ca4cba pins that generic Employment persistence must stop depending on candidate conversion. Any GREEN repair must preserve this issue's real concurrency guarantee with an equivalent People-owned conflict boundary (for example, a tenant+Person transaction lock/root that is independent of recruiting provenance), and Assignment must share that boundary before portfolio snapshot validation. Do not reintroduce the previously invalid redundant Employment lock from #271, and do not widen a recruiting provenance relation into generic worker authority.

    Accordingly, keep the two-session acceptance and failure-cleanup evidence here. What becomes stale is only the statement that the conversion row is the permanent canonical worker lock. When #64 replaces that coupling, this issue's mechanism test must ordinary-forward to the replacement exact conflict boundary and prove the same accepted/rejected outcomes, lock evidence, audit/outbox/idempotency atomicity, and quiescence.

  6. changed the title [-]test(people): prove Assignment conflict serialization with real PostgreSQL interleavings[/-] [+]test(people): prove Assignment Employment/Position conflict serialization with real PostgreSQL interleavings[/+] on Sep 12, 2026
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