Repository navigation
Feat/7 proposal review approval - #70
Merged
Merged
Conversation
…w-approval # Conflicts: # services/work-management/src/eventflow_work_management/api/app.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
If-Match.Idempotency-Key.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
Implementation
1. Proposal Decision API
Implemented:
POST /v1/workspaces/{workspace_id}/proposals/{proposal_id}/decideThe endpoint supports three explicit decisions:
approvedrejectedchanges_requestedThe API integrates with the existing Work Management contracts and uses strict request validation.
Implemented in:
api/approval_routes.pyservices/approval.pyThe endpoint enforces:
If-Matchpreconditions.Idempotency-Key.2. Approval Service
Added
ApprovalServiceandApprovalOutcome.The service coordinates:
The implementation reuses the existing repository and domain infrastructure rather than introducing a separate approval subsystem.
3. Approval Lifecycle
The workflow now supports:
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:
The earlier review decision remains part of the historical record.
A stale revision produces an explicit
STALE_VERSIONerror.A stale
If-Matchprecondition returns HTTP 412.5. Role-Based Authorization
Approval is restricted to the existing authorized reviewer roles:
approveradminownerContributors 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.pyrepositories/work_item_repository.pyrepositories/board_repository.pyThe approval repository now coordinates the complete approval transaction.
The transaction includes:
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_proposalbuild_cardThese 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:
IDEMPOTENCY_CONFLICTIdempotent replay preserves:
approval_idwork_item_idcard_idNo duplicate executable work is created.
The implementation uses the existing durable idempotency infrastructure.
8. Optimistic and Pessimistic Concurrency Protection
The implementation combines:
If-Matchoptimistic concurrency.SELECT ... FOR UPDATErow locking.The database enforces:
uq_approvals_proposal_versionThis 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
readycolumn.The card:
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:
uq_approvals_proposal_versionuq_idempotency_records_scope_keyuq_work_items_origin_proposaltrg_work_items_origin_guarduq_board_cards_column_rankNo historical migrations were modified.
Files Changed
Added
services/work-management/src/eventflow_work_management/api/approval_routes.pyservices/work-management/src/eventflow_work_management/services/approval.pyservices/work-management/tests/test_approval_decision_integration.pyModified
services/work-management/src/eventflow_work_management/api/app.pyservices/work-management/src/eventflow_work_management/repositories/approval_repository.pyservices/work-management/src/eventflow_work_management/repositories/board_repository.pyservices/work-management/src/eventflow_work_management/repositories/work_item_repository.pyTotal: 7 files changed.
Testing
Tests were executed using
jsr-testrunneragainst PostgreSQL.Results
The skipped test requires a live Keycloak environment and is unrelated to Issue #7.
New Integration Tests
Added:
test_approval_decision_integration.py10 integration tests covering the approval workflow.
Verified scenarios include:
If-Matchrequests return HTTP 412.Concurrency Evidence
test_concurrent_decisions_have_exactly_one_effectTwo threads submit decisions for the same Proposal using different idempotency keys.
Observed result:
AlreadyDecidedoutcome.Final database state:
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_failureAn approval is attempted with Board placement referencing another Project.
The operation fails during the transaction.
Observed final database state:
The transaction does not leave partially authorized work.
Contract Validation
Passed:
python scripts/validate_contracts.pyopenapi-spec-validatorValidated schemas include:
ApprovalWorkItemBoardCardDecideProposalRequestDecideProposalResponseThe implementation preserves the accepted decision values:
approvedrejectedchanges_requestedNo public contract changes were required for the implemented decision path.
Quality Checks
git diff --checkpassed.Security Considerations
The implementation enforces:
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
DecideProposalRequestdoes 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:
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
readycolumn.The architecture documentation contains inconsistent references to
BacklogandReady.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
Review Focus
Reviewers should pay particular attention to:
Git Information
Branch:
feat/7-proposal-review-approvalCommit:
f1590f8bcbe8a426aeb9d47bd664ef5c42dc69f6Commit message:
feat(work-management): implement proposal review and approval workflow (#7)Base dependency:
9425a35— feat(work-management): implement consultation task proposals (#6)Local status:
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.