Skip to content

fix: prevent proposal shortlisting and acceptance race conditions - #261

Open
tim-dev212 wants to merge 1 commit into
Lumina-eX:mainfrom
tim-dev212:feat/issue-243-proposal-shortlisting-acceptance-concurrency
Open

tim-dev212 wants to merge 1 commit into
Lumina-eX:mainfrom
tim-dev212:feat/issue-243-proposal-shortlisting-acceptance-concurrency

Conversation

@tim-dev212

Copy link
Copy Markdown

Overview

This PR hardens the proposal shortlisting and acceptance workflow against race conditions and data consistency issues. It adds ownership and status validation, wraps shortlist/accept/reject actions in database transactions, enforces a single accepted proposal per single-award project via database constraints, records acceptance/rejection outcomes consistently, and makes duplicate or conflicting requests return predictable results.

Related Issue

Changes

🔒 Proposal Action Concurrency Control

  • [MODIFY] app/api/proposals/[id]/route.ts

    • Validate project ownership and current proposal status before processing accept/reject actions.
    • Run acceptance and rejection inside a database transaction so related updates commit or roll back atomically.
    • Return consistent, predictable responses for duplicate or already-processed requests.
  • [MODIFY] app/api/projects/[id]/proposals/route.ts

    • Validate ownership and proposal status before shortlisting.
    • Perform shortlist updates within a transaction to avoid conflicting concurrent writes.
  • [MODIFY] lib/db.ts

    • Add transaction helper support used by the proposal action handlers.
  • [MODIFY] lib/audit-log.ts

    • Record acceptance and rejection outcomes consistently in the audit log.
  • [ADD] lib/db/migrations/010_project_proposals.sql

    • Add database constraints enforcing a single accepted proposal per single-award project.
  • [ADD] lib/db/migrations/010_proposals.sql

    • Add supporting constraints/indexes for proposal status integrity.

Verification Results

Manual concurrency checks:
✅ Concurrent acceptance requests produce a single contract for the project
✅ Failed operations roll back without partial data changes
✅ Duplicate requests return a consistent result
✅ Unauthorized users cannot shortlist, accept, or reject
✅ Actions on already accepted/rejected proposals are rejected
✅ Single-award project holds at most one accepted proposal
✅ Acceptance/rejection outcomes recorded in DB and audit logs
Acceptance Criteria Status
Concurrent acceptance requests cannot create multiple contracts for the same project ✅ Enforced via transaction + DB constraint
Failed operations roll back completely ✅ Transactional updates across related entities
Repeated/duplicate requests return a consistent result ✅ Status validation short-circuits reprocessing
Only authorized project owners can shortlist/accept/reject ✅ Ownership validated before action
Status validation prevents actions on invalid proposals ✅ Accepted/rejected/invalid states blocked
Single-award project has only one accepted proposal ✅ Database constraint enforced
Acceptance/rejection recorded consistently in DB and audit logs ✅ Audit log updated within transaction
Transactions ensure atomic updates across related entities ✅ Wrapped in lib/db.ts transaction helper
Appropriate loading/success/error responses returned ✅ Consistent API responses
Concurrency conflicts handled gracefully ✅ Conflicts surface as predictable errors, no partial state

Closes #243

@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@tim-dev212 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

[Feature]: Proposal Shortlisting & Acceptance Concurrency Control

1 participant