Skip to content

Feat/7 proposal review approval - #70

Merged
jacksonsr451 merged 3 commits into
mainfrom
feat/7-proposal-review-approval
Oct 18, 2026
Merged

jacksonsr451 merged 3 commits into
mainfrom
feat/7-proposal-review-approval

Conversation

@jacksonsr451

Copy link
Copy Markdown
Owner

Summary

Closes #7

Implements the proposal review and decision workflow for EventFlow's Work Management bounded context, introducing explicit, role-based approval, rejection, and change-request decisions.

An authorized approval now atomically creates an Approval record, WorkItem, and BoardCard, while rejection and change requests preserve the proposal review history without creating executable work.

The implementation includes:

  • Role-based approval authorization.
  • Exact Proposal revision binding.
  • Optimistic concurrency using If-Match.
  • Mandatory Idempotency-Key.
  • Self-approval prevention.
  • Atomic Approval, WorkItem, and BoardCard creation.
  • Durable idempotent response replay.
  • Append-only audit records.
  • Transactional rollback protection.
  • PostgreSQL concurrency and integration tests.

The implementation preserves the existing API contracts, domain architecture, and Proposal lifecycle introduced by Issues #4, #5, and #6.

Related Issues

Closes #7

Depends on:

Type of Change

  • New feature
  • Backend implementation
  • Security enforcement
  • Transactional consistency
  • Concurrency protection
  • Integration tests
  • Database migration
  • Breaking API change
  • Frontend implementation

Implementation

1. Proposal Decision API

Implemented:

POST /v1/workspaces/{workspace_id}/proposals/{proposal_id}/decide

The endpoint supports three explicit decisions:

  • approved
  • rejected
  • changes_requested

The API integrates with the existing Work Management contracts and uses strict request validation.

Implemented in:

  • api/approval_routes.py
  • services/approval.py

The endpoint enforces:

  • Authentication.
  • Workspace authorization.
  • Proposal lifecycle validation.
  • Exact Proposal version.
  • If-Match preconditions.
  • Mandatory Idempotency-Key.
  • Self-approval restrictions.
  • Explicit domain error responses.

2. Approval Service

Added ApprovalService and ApprovalOutcome.

The service coordinates:

  • Authorization.
  • Proposal validation.
  • Review decisions.
  • Request fingerprint generation.
  • Idempotency.
  • Transactional persistence.
  • Decision response construction.

The implementation reuses the existing repository and domain infrastructure rather than introducing a separate approval subsystem.

3. Approval Lifecycle

The workflow now supports:

Consultation
    |
    v
Manual Triage
    |
    v
Proposal (DRAFT)
    |
    v
Proposal (IN_REVIEW)
    |
    +----------------------+
    |                      |
    v                      v
CHANGES_REQUESTED        REJECTED
    |                      |
    v                      |
Proposal Edit              |
    |                      |
    v                      |
New Revision               |
    |                      |
    v                      |
IN_REVIEW                  |
    |                      |
    v                      |
APPROVED                   |
    |                      |
    v                      |
WorkItem                   |
    |                      |
    v                      |
BoardCard                  |
                           |
                      No WorkItem
                      No BoardCard

Approval is the only supported path that authorizes creation of executable work.

Rejected and changes-requested proposals do not create WorkItems or BoardCards.

4. Exact Proposal Revision Binding

Approval decisions reference the exact Proposal revision.

The implementation prevents an approval from authorizing a different revision of the same Proposal.

Example:

Proposal P1
    |
    +-- Version 1
    |      |
    |      +-- CHANGES_REQUESTED
    |
    +-- Version 2
           |
           +-- APPROVED
                  |
                  +-- WorkItem
                  |
                  +-- BoardCard

The earlier review decision remains part of the historical record.

A stale revision produces an explicit STALE_VERSION error.

A stale If-Match precondition returns HTTP 412.

5. Role-Based Authorization

Approval is restricted to the existing authorized reviewer roles:

  • approver
  • admin
  • owner

Contributors without approval permission are rejected.

Self-approval is explicitly prevented.

The implementation preserves workspace and project isolation.

Authorization failures do not create Approval records, WorkItems, or BoardCards.

6. Atomic Approval and Work Materialization

Updated:

  • repositories/approval_repository.py
  • repositories/work_item_repository.py
  • repositories/board_repository.py

The approval repository now coordinates the complete approval transaction.

The transaction includes:

  1. Proposal validation and locking.
  2. Idempotency validation.
  3. Approval record creation.
  4. Proposal status transition.
  5. WorkItem creation.
  6. Initial BoardCard creation.
  7. Audit record persistence.
  8. Idempotency completion.
  9. Database commit.

All effects are committed together.

If any required operation fails, the transaction is rolled back.

The implementation introduces flush-only construction methods:

  • build_from_approved_proposal
  • build_card

These methods allow the approval repository to control the transaction boundary without intermediate commits.

7. Durable Idempotency

The decision endpoint requires an Idempotency-Key.

Supported behavior:

Scenario Result
First valid request Decision persisted
Same key and same request Original result replayed
Same key with different request IDEMPOTENCY_CONFLICT
Concurrent requests At most one successful decision
Retry after successful approval Existing Approval, WorkItem, and BoardCard returned

Idempotent replay preserves:

  • approval_id
  • work_item_id
  • card_id

No duplicate executable work is created.

The implementation uses the existing durable idempotency infrastructure.

8. Optimistic and Pessimistic Concurrency Protection

The implementation combines:

  • If-Match optimistic concurrency.
  • Exact Proposal revision validation.
  • SELECT ... FOR UPDATE row locking.
  • Existing database uniqueness constraints.

The database enforces:

uq_approvals_proposal_version

This prevents multiple approval decisions from being persisted for the same Proposal revision.

Concurrent requests with different idempotency keys cannot create conflicting successful decisions.

9. Auditability

The approval transaction persists audit records with correlation information.

The audit trail records decision activity and the creation of downstream executable work.

Review decisions remain associated with the Proposal and its exact revision.

The implementation reuses the existing append-only audit infrastructure.

10. BoardCard Integrity

Successful approval creates the initial BoardCard in the ready column.

The card:

  • References an existing WorkItem.
  • Uses the selected Board.
  • Uses the existing BoardCard persistence model.
  • Receives an opaque, collision-resistant rank.
  • Is persisted in the same transaction as the Approval and WorkItem.

The implementation does not permit a successful approval to leave an orphaned BoardCard.

Database and Migration

No new Alembic migration was required.

The implementation reuses constraints established by migration 0001.

Relevant constraints and safeguards:

Constraint Purpose
uq_approvals_proposal_version One decision per Proposal revision
uq_idempotency_records_scope_key Durable idempotency key uniqueness
uq_work_items_origin_proposal Prevent duplicate WorkItems for one Proposal
trg_work_items_origin_guard Require a matching approved Approval
BoardCard foreign keys Preserve referential integrity
uq_board_cards_column_rank Prevent duplicate ranks within a column

No historical migrations were modified.

Files Changed

Added

  • services/work-management/src/eventflow_work_management/api/approval_routes.py
  • services/work-management/src/eventflow_work_management/services/approval.py
  • services/work-management/tests/test_approval_decision_integration.py

Modified

  • services/work-management/src/eventflow_work_management/api/app.py
  • services/work-management/src/eventflow_work_management/repositories/approval_repository.py
  • services/work-management/src/eventflow_work_management/repositories/board_repository.py
  • services/work-management/src/eventflow_work_management/repositories/work_item_repository.py

Total: 7 files changed.

Testing

Tests were executed using jsr-testrunner against PostgreSQL.

Results

Test Suite Passed Failed Skipped
Work Management 157 0 1
Root contract/security suite 114 0 0
Total 271 0 1

The skipped test requires a live Keycloak environment and is unrelated to Issue #7.

New Integration Tests

Added:

test_approval_decision_integration.py

10 integration tests covering the approval workflow.

Verified scenarios include:

  • Approval creates exactly one WorkItem and BoardCard.
  • Repeated requests return the same resource identifiers.
  • Reusing an idempotency key with a different request fails.
  • Stale Proposal versions are rejected.
  • Stale If-Match requests return HTTP 412.
  • Self-approval is rejected.
  • Rejection does not create executable work.
  • Changes requested does not create executable work.
  • Unauthorized approval returns HTTP 403.
  • Invalid placement or missing required request data fails.
  • Approval of a revised Proposal uses the new revision.
  • Concurrent decisions produce one successful outcome.
  • Transaction failure rolls back all created records.

Concurrency Evidence

test_concurrent_decisions_have_exactly_one_effect

Two threads submit decisions for the same Proposal using different idempotency keys.

Observed result:

  • One successful decision.
  • One AlreadyDecided outcome.

Final database state:

Approval records: 1
WorkItems:        1
BoardCards:       1

The implementation uses Proposal row locking and the database uniqueness constraint to enforce the invariant.

Transaction Rollback Evidence

test_approval_rolls_back_every_row_on_failure

An approval is attempted with Board placement referencing another Project.

The operation fails during the transaction.

Observed final database state:

Approval records:     0
WorkItems:            0
BoardCards:           0
Idempotency records:  0

The transaction does not leave partially authorized work.

Contract Validation

Passed:

  • python scripts/validate_contracts.py
  • OpenAPI specification validation using openapi-spec-validator
  • Runtime DTO comparison against existing contract definitions

Validated schemas include:

  • Approval
  • WorkItem
  • BoardCard
  • DecideProposalRequest
  • DecideProposalResponse

The implementation preserves the accepted decision values:

  • approved
  • rejected
  • changes_requested

No public contract changes were required for the implemented decision path.

Quality Checks

  • Work Management tests passed.
  • Root contract/security tests passed.
  • PostgreSQL integration tests passed.
  • Concurrency test passed.
  • Transaction rollback test passed.
  • Contract validation passed.
  • OpenAPI validator passed.
  • Pre-commit hooks passed.
  • git diff --check passed.
  • Working tree clean.
  • Remote CI verification pending.

Security Considerations

The implementation enforces:

  • Authenticated decision requests.
  • Workspace-scoped resource access.
  • Reviewer role authorization.
  • Self-approval prevention.
  • Exact Proposal revision binding.
  • Optimistic concurrency.
  • Row-level locking.
  • Durable idempotency.
  • Transactional rollback.
  • WorkItem authorization provenance.
  • BoardCard referential integrity.
  • Append-only audit records.

No direct unauthenticated or unauthorized WorkItem creation path was introduced.

Known Limitations and Architectural Reservations

1. Existing WorkItem Association

Issue #7 mentions approving a Proposal by creating a WorkItem or associating an existing WorkItem.

The current accepted DecideProposalRequest does not expose an existing WorkItem identifier.

The implemented path creates a new WorkItem and BoardCard.

Associating an existing WorkItem remains unsupported.

Supporting this path requires an explicit architectural decision and contract extension covering:

  • Association eligibility.
  • Workspace and Project consistency.
  • Existing approval provenance.
  • WorkItem lifecycle restrictions.
  • Duplicate association prevention.
  • BoardCard reuse.
  • Transactional consistency.

This functionality is not claimed as implemented.

2. Single-User Self-Approval Mode

The architecture documents an exception for single-user workspaces.

The current implementation rejects self-approval unconditionally.

The exception requires a trusted workspace configuration mechanism and an approved policy implementation.

3. Initial Board Column

BoardCards are initially placed in the ready column.

The architecture documentation contains inconsistent references to Backlog and Ready.

The implementation follows the established Ready behavior.

Configurable initial column selection is not included.

4. Transactional Outbox

The approval implementation uses the existing audit infrastructure.

No dedicated Work Management transactional outbox was introduced.

External event delivery is outside the implemented scope.

5. Board Management API

No HTTP endpoint for Board creation was introduced.

Integration tests prepare Boards through the existing repository infrastructure.

Acceptance Criteria

Requirement Status
Explicit approval Implemented
Explicit rejection Implemented
Changes requested Implemented
Role-based authorization Implemented
Exact-version approval Implemented
Second review after revision Implemented
Optimistic concurrency Implemented
Mandatory idempotency Implemented
Audit records Implemented
Atomic WorkItem creation Implemented
Atomic BoardCard creation Implemented
Prevention of orphaned cards Implemented
Concurrency tests Passed
Authorization tests Passed
Transaction rollback tests Passed
Contract validation Passed
Existing WorkItem association Not implemented — architectural reservation
Single-user self-approval exception Not implemented — architectural reservation
Remote CI Pending

Review Focus

Reviewers should pay particular attention to:

  1. Approval transaction boundaries.
  2. Idempotency replay behavior.
  3. Proposal row locking and concurrent decisions.
  4. WorkItem authorization provenance.
  5. BoardCard creation and rollback behavior.
  6. Exact Proposal revision validation.
  7. Reviewer authorization and self-approval restrictions.
  8. Existing WorkItem association requirements.
  9. Single-user workspace approval policy.
  10. Initial Board column semantics.

Git Information

Branch: feat/7-proposal-review-approval

Commit:

f1590f8bcbe8a426aeb9d47bd664ef5c42dc69f6

Commit message:

feat(work-management): implement proposal review and approval workflow (#7)

Base dependency:

9425a35 — feat(work-management): implement consultation task proposals (#6)

Local status:

  • Implementation committed.
  • Working tree clean.
  • No push performed.
  • No merge performed.
  • Remote CI not yet verified.

Final Outcome

Issue #7 introduces the authoritative proposal decision workflow for EventFlow.

Authorized reviewers can approve, reject, or request changes against an exact Proposal revision.

Successful approval atomically creates the corresponding Approval, WorkItem, and BoardCard records.

Idempotency, optimistic concurrency, database constraints, and transaction rollback prevent duplicate or partially materialized executable work.

Local verification: 271 passed, 0 failed, 1 skipped.

Review status: READY FOR PR WITH RESERVATIONS.

The remaining architectural limitations are explicitly documented above and must be resolved or formally accepted before treating every clause of Issue #7 as complete.

@jacksonsr451
jacksonsr451 merged commit 6d75c8a into main Oct 18, 2026
3 checks passed
@jacksonsr451
jacksonsr451 deleted the feat/7-proposal-review-approval branch October 18, 2026 02:09
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.

[FEAT] Implementar revisão, aprovação e rejeição de propostas

1 participant