# Kế hoạch hardening transaction cho workflow persisted dossier review

Tài liệu này tập trung xử lý dứt điểm vấn đề partial state trong `WorkflowPersistenceService.start_review_run`, bám trực tiếp vào cách DSCons hiện đang ghi dữ liệu qua `app/services/workflow_persistence_service.py`, `app/core/postgres.py`, `app/services/workflow_policy_service.py`, test `tests/test_workflow_persistence_service.py`, và contract nghiệp vụ trong `docs/dossier-review-workflow-schema.md`.

## 1. Tóm tắt điều đang xảy ra

Hiện tại `start_review_run()` điều phối một chuỗi side effect nhiều bước:

1. `create_dossier_review_session`
2. lặp từng finding:
   - `create_dossier_review_finding`
   - có thể `assign_dossier_review_finding`
   - có thể `submit_dossier_review_supplement`
   - có thể `verify_dossier_review_finding`
3. có thể `close_dossier_review_session`
4. `_persist_employee_logs`
   - `upsert_employee_work_log_session`
   - `append_employee_work_log_action`
5. `fetch_dossier_review_sessions`
6. enrichment operational state fail-soft

Mỗi call trong `PostgresClient` hiện tự mở connection, tự commit transaction riêng, rồi trả kết quả. Điều này có nghĩa là `start_review_run()` là một orchestration ở application layer nhưng **không có transaction boundary end-to-end** ở persistence layer.

Kết quả: chỉ cần lỗi ở giữa chuỗi là hệ thống có thể lưu một phần review workflow nhưng không lưu phần còn lại, tạo ra persisted state nửa chừng.

---

## 2. Root-cause sâu

## 2.1. Root-cause kiến trúc

### A. Transaction boundary bị đặt quá thấp
`PostgresClient` hiện commit theo từng method:
- `create_dossier_review_session()` commit riêng
- `create_dossier_review_finding()` commit riêng
- `assign_dossier_review_finding()` commit riêng
- `submit_dossier_review_supplement()` commit riêng
- `verify_dossier_review_finding()` commit riêng
- `close_dossier_review_session()` commit riêng
- `upsert_employee_work_log_session()` commit riêng
- `append_employee_work_log_action()` commit riêng

Nhưng nghiệp vụ thực sự của `start_review_run()` không phải là từng bước độc lập; nó là một **workflow transaction nhiều bước**.

### B. Có nhiều aggregate cùng bị mutate trong một run
Một lần start workflow hiện chạm vào ít nhất 2 vùng dữ liệu:
- dossier review aggregate: session / findings / assignments / submissions / actions / snapshots
- employee work log aggregate: work_log_sessions / work_log_actions

Nếu lỗi xảy ra sau khi review aggregate đã commit nhưng trước khi employee logs commit xong, output trả về sẽ không còn phản ánh đúng side effects đã hoặc chưa xảy ra.

### C. Trộn bước idempotent và non-idempotent trong cùng một orchestration
Một số step có tính gần-idempotent hoặc có dedup tương đối:
- `upsert_employee_work_log_session`
- `append_employee_work_log_action` có `external_ref` + unique-like conflict path
- fetch lại session
- operational state enrichment không mutate DB

Một số step là non-idempotent hoặc idempotent yếu:
- `create_dossier_review_session` dùng `review_code` sinh theo timestamp, retry sẽ tạo review mới
- `create_dossier_review_finding` không thấy idempotency key rõ ràng
- `assign_dossier_review_finding` tạo assignment mới
- `submit_dossier_review_supplement` tạo submission mới
- `verify_dossier_review_finding` mutate trạng thái
- `close_dossier_review_session` có side effect đóng findings/assignments mở

Do chưa phân loại rõ, retry sau lỗi có thể nhân bản hoặc làm lệch lifecycle.

### D. Không có checkpoint/workflow state riêng
`WorkflowReviewRunSummary` có `lifecycle_outcomes`, `resumable_from_step`, `correlation_id`, `run_id`, nhưng đó là response object, không phải persisted run ledger. Nếu request fail giữa chừng:
- không có workflow execution record persisted để biết step nào đã xong
- không có checkpoint chuẩn để resume an toàn
- không có step status machine để vận hành đọc lại

### E. Compensation hiện chỉ là implicit, không phải explicit
Có một số “compensation mềm”:
- nếu policy block thì không assign
- nếu enrichment fail thì degrade response

Nhưng không có compensation thật cho partial writes như:
- session đã tạo nhưng chưa có findings đầy đủ
- findings đã tạo nhưng chưa có assignments
- assignments đã tạo nhưng chưa submit/verify
- review đã đóng nhưng employee logs chỉ ghi một phần

### F. Interface persistence chưa hỗ trợ transaction injection
`WorkflowPersistenceService` chỉ gọi vào methods mức cao của `PostgresClient`. Không thấy:
- transaction context object
- shared connection/cursor injection
- unit-of-work wrapper
- batch persistence API cho workflow run

Do đó orchestration không thể “ôm” toàn bộ chuỗi DB writes trong một transaction.

---

## 2.2. Root-cause vận hành / observability

### A. Thiếu telemetry cho workflow run
Hiện response có:
- `correlation_id=review_code`
- `run_id=request.metadata.get("diagnostic_run_id")`

Nhưng không có persisted telemetry chuẩn cho:
- workflow step bắt đầu / kết thúc / lỗi
- thời lượng từng step
- DB transaction retry
- partial commit detection
- reconciliation required

### B. Không có trạng thái “workflow persistence degraded / incomplete”
`automation_status` hiện chủ yếu mô tả business automation:
- `completed`
- `manual_follow_up_required`

Nó không mô tả persistence integrity:
- committed
- incomplete
- compensation_pending
- reconciliation_required
- replay_safe / replay_unsafe

### C. Test hiện mới xác nhận happy path và fail-soft enrichment
`tests/test_workflow_persistence_service.py` xác nhận:
- persist full flow và normalize logs
- enrichment fail thì fallback snapshot

Chưa có test cho:
- fail tại assignment/submission/verify/logging
- retry cùng request
- crash sau session create
- crash trước close
- crash giữa employee log actions
- deterministic resume

---

## 3. Taxonomy partial-state trong DSCons

## 3.1. Partial state nội bộ trong dossier review aggregate
Ví dụ:
- session đã tạo, finding chưa tạo đủ
- finding đã tạo, assignment chưa có
- assignment đã tạo, submission chưa có
- submission đã có, verify chưa xong
- verify xong một phần findings, close chưa chạy

Loại này có thể chấp nhận tạm thời nếu được mô hình hóa thành workflow execution state rõ ràng. Hiện tại chưa có.

## 3.2. Partial state giữa review aggregate và work log aggregate
Ví dụ:
- review closed nhưng employee work log chưa phản ánh
- work log đã có action “auto verify” nhưng finding thực tế chưa verify

Đây là loại nguy hiểm hơn vì gây semantic mismatch giữa lớp review và operational logs.

## 3.3. Partial state do retry không idempotent
Ví dụ:
- request timeout sau khi session commit; client retry tạo session mới
- crash sau khi finding 1,2 đã tạo; replay tạo thêm finding trùng code
- log actions bị append trùng

## 3.4. Partial state do step ngoài core persistence
Ví dụ:
- enrichment operational state fail sau commit
- tương lai có projection/report/remediation đọc ngay sau commit nhưng gặp dữ liệu chưa “ổn định”

Enrichment hiện đã fail-soft đúng hướng, nhưng lại làm mờ fact là persistence chính chưa có explicit “commit completed” marker.

---

## 4. Target architecture

Đích nên là mô hình **hybrid transaction + checkpointed saga**, không phải chỉ một transaction thuần túy cho mọi thứ.

## 4.1. Nguyên tắc cốt lõi

### Principle 1: Atomic trong một aggregate DB-local
Toàn bộ ghi dữ liệu thuộc **dossier review aggregate** cho một lần start run nên được commit atomically trong **một PostgreSQL transaction duy nhất** khi còn cùng database.

Bao gồm:
- session
- baseline snapshot
- findings
- assignments
- submissions
- verification mutations
- review actions
- close session

### Principle 2: Tách post-commit side effects thành checkpointed steps
Các bước không cần nằm trong critical transaction:
- employee work log projection
- operational state enrichment
- các projection/report/remediation downstream sau này

Các bước này nên chạy **sau commit**, có checkpoint riêng, retry được, và có thể reconciliation.

### Principle 3: Introduce workflow run execution ledger
Cần có persisted run state cho orchestration, tối thiểu:
- run_id
- idempotency_key
- review_code/review_id
- request_fingerprint
- current_step
- step_status map
- overall_status
- last_error
- started_at / updated_at / completed_at

### Principle 4: Retry chỉ áp dụng cho step idempotent hoặc có dedup key
Retry an toàn phải dựa trên:
- idempotency key ở level workflow run
- deterministic business keys ở level finding/assignment/submission/log action nếu có
- explicit step status

### Principle 5: Compensation chỉ dùng cho side effects ngoài core aggregate
Với dữ liệu core review trong cùng Postgres, rollback transaction tốt hơn compensation.
Compensation chỉ cần cho:
- post-commit projections
- employee log actions nếu chọn tách khỏi transaction chính
- external integrations trong tương lai

---

## 4.2. Mô hình target flow

### Phase A: Acquire / resume workflow execution
1. Nhận `WorkflowReviewStartRequest`
2. Tính `idempotency_key`
3. Upsert hoặc fetch `workflow_run_execution`
4. Nếu run đã `completed`, trả kết quả đã persist hoặc materialize lại summary
5. Nếu run đang `in_progress` nhưng stale, cho phép recovery/resume theo checkpoint
6. Nếu run mới, set status `preparing`

### Phase B: Core review transaction
Trong **một DB transaction**:
1. create/fetch review session theo idempotency contract
2. create/fetch findings
3. create/fetch assignments
4. create/fetch submissions
5. verify findings nếu policy cho phép
6. close review nếu điều kiện thỏa
7. ghi action/audit tương ứng
8. update workflow execution checkpoint = `core_committed`

Nếu lỗi ở bất kỳ bước nào trong Phase B:
- rollback toàn bộ core transaction
- workflow execution status = `failed`
- không để lại half-written review aggregate

### Phase C: Post-commit projections
Sau khi Phase B commit:
1. persist employee work logs
2. update execution checkpoint `employee_logs_committed`
3. fetch review summary
4. build response
5. optional enrichment operational state
6. mark execution `completed` hoặc `completed_with_degraded_projections`

Nếu lỗi ở employee logs:
- không rollback core review
- mark execution `projection_degraded`
- tạo reconciliation/resume task
- response phải phản ánh degraded persistence state rõ ràng

---

## 4.3. Step classification: idempotent vs non-idempotent

## A. Non-idempotent hoặc idempotent yếu cần hardening mạnh
1. `create_dossier_review_session`
2. `create_dossier_review_finding`
3. `assign_dossier_review_finding`
4. `submit_dossier_review_supplement`
5. `verify_dossier_review_finding`
6. `close_dossier_review_session`

Cách harden:
- chạy trong cùng transaction
- dùng deterministic key hoặc execution ledger để tránh duplicate
- chỉ commit khi core workflow hoàn tất

## B. Idempotent / retry-friendly hơn
1. `upsert_employee_work_log_session`
2. `append_employee_work_log_action` nếu external_ref deterministic
3. `fetch_dossier_review_sessions`
4. `_build_operational_state_enrichment`

Cách harden:
- tách post-commit
- retry theo step
- nếu fail thì mark degraded và reconcile

---

## 4.4. Checkpoint model đề xuất

Các checkpoint tối thiểu cho `start_review_run`:

- `execution_created`
- `policy_evaluated`
- `core_transaction_started`
- `review_session_prepared`
- `findings_persisted`
- `assignments_persisted`
- `submissions_persisted`
- `verifications_persisted`
- `review_closed` hoặc `review_left_open`
- `core_committed`
- `employee_logs_persisted`
- `response_materialized`
- `completed`

Các checkpoint này không nhất thiết cần row per event ngay từ đầu; có thể bắt đầu bằng:
- `current_step`
- `completed_steps[]`
- `step_attempts{}`
- `last_error`

---

## 4.5. Compensation model đề xuất

## Core aggregate
Không dùng compensation business-level cho lỗi nội bộ cùng DB; dùng rollback transaction.

## Post-commit employee logs
Nếu employee log fail:
- không xóa review aggregate
- ghi execution status `reconciliation_required`
- cho phép replay riêng step employee log bằng run_id/review_id
- external_ref phải deterministic để replay không nhân bản action

## Future external systems
Nếu sau này có notify, queue, external dashboards:
- nên áp dụng outbox pattern thay vì gọi trực tiếp trong core path

---

## 5. Thay đổi interface tối thiểu

Mục tiêu là hardening mà không phá rộng codebase.

## 5.1. Ở `PostgresClient`
Bổ sung khả năng transaction-scoped operations, theo hướng tối thiểu:

### Option tối thiểu nhất
- thêm helper `transaction()` hoặc `get_connection()` dùng bởi service orchestration
- các method persistence hiện tại nhận optional `connection`/`cursor`

Ví dụ contract ở mức ý tưởng:
- `create_dossier_review_session(payload, cursor=None)`
- `create_dossier_review_finding(review_id, payload, cursor=None)`
- `assign_dossier_review_finding(review_id, finding_id, payload, cursor=None)`
- ...

Nếu có `cursor`, method không tự commit.
Nếu không có `cursor`, vẫn giữ behavior cũ để backward compatible.

### Lý do
- đổi ít interface
- không ép refactor toàn repo sang repository/unit-of-work ngay
- đủ để `WorkflowPersistenceService.start_review_run` gom core writes vào một transaction

## 5.2. Ở `WorkflowPersistenceService`
Thay interface đầu vào tối thiểu theo hướng additive:
- hỗ trợ `request.metadata.idempotency_key`
- nếu không có, service tự derive key từ request fingerprint
- support `resume_mode`/`allow_resume` qua metadata hoặc optional service parameter nội bộ

Không nên đổi shape lớn của `WorkflowReviewStartRequest` ngay nếu muốn rollout ít rủi ro; có thể đọc key từ metadata trước.

## 5.3. Ở response summary
Bổ sung trường additive, không phá contract cũ:
- `persistence_status`: `committed` | `projection_degraded` | `failed`
- `execution_status`: `completed` | `completed_with_warnings` | `reconciliation_required`
- `workflow_run_id`
- `completed_checkpoints`
- `reconciliation_required` bool

Các field cũ như `automation_status` vẫn giữ để tách business automation khỏi persistence integrity.

---

## 6. Design decisions và tradeoff

## Decision 1: Dùng single DB transaction cho core review aggregate
**Chọn:** Có.

**Lợi ích**
- loại bỏ phần lớn partial state nguy hiểm nhất
- semantics đơn giản
- không cần compensation cho core write path

**Tradeoff**
- transaction dài hơn
- lock giữ lâu hơn nếu request có nhiều findings
- cần refactor methods `PostgresClient` để reuse connection/cursor

**Kết luận**
Đáng làm vì tất cả core writes hiện đều ở cùng PostgreSQL.

---

## Decision 2: Không nhét employee logs vào cùng transaction core ở phiên bản đích
**Chọn:** Không, tách post-commit.

**Lợi ích**
- giảm transaction time
- tách aggregate chính khỏi projection vận hành
- dễ reconcile, phù hợp hướng agent/report sau này

**Tradeoff**
- vẫn tồn tại eventual consistency giữa review và logs
- cần execution ledger + retry/reconciliation

**Kết luận**
Đây là tradeoff tốt hơn “all-in-one transaction”, vì work logs thiên về projection/audit phụ hơn là state machine cốt lõi của review.

---

## Decision 3: Dùng execution ledger thay vì chỉ rely vào response `lifecycle_outcomes`
**Chọn:** Có.

**Lợi ích**
- resume được
- vận hành tra cứu được
- telemetry tốt hơn
- nền tảng cho E2E verification agent khác xây

**Tradeoff**
- thêm schema/table mới
- cần migration và cleanup policy

**Kết luận**
Bắt buộc nếu muốn xử lý dứt partial state thay vì chỉ giảm xác suất.

---

## Decision 4: Idempotency key additive qua metadata trước, chưa đổi schema request mạnh
**Chọn:** Có.

**Lợi ích**
- rollout an toàn
- API clients cũ không vỡ
- cho phép dần chuẩn hóa contract sau

**Tradeoff**
- metadata-based contract ít discoverable hơn field typed riêng
- về lâu dài vẫn nên nâng lên field typed chính thức

**Kết luận**
Phù hợp phase đầu; sau ổn định có thể promote thành field typed.

---

## Decision 5: Outbox cho tương lai, chưa bắt buộc ở phase 1
**Chọn:** Chưa bắt buộc cho issue #1.

**Lợi ích**
- tránh scope creep
- tập trung xử lý partial state nội bộ trước

**Tradeoff**
- future external side effects vẫn cần pattern khác

**Kết luận**
Chỉ đưa vào roadmap sau khi core transaction + ledger ổn định.

---

## 7. Migration strategy an toàn

## 7.1. Data model tối thiểu cần thêm
Nên thêm bảng execution riêng, ví dụ logic:
- `workflow_run_executions`
  - `workflow_run_id`
  - `workflow_type`
  - `idempotency_key`
  - `request_payload`
  - `request_fingerprint`
  - `project_code`
  - `review_code`
  - `review_id`
  - `status`
  - `current_step`
  - `completed_steps`
  - `step_attempts`
  - `last_error`
  - `reconciliation_required`
  - `created_at`
  - `updated_at`
  - `completed_at`

Tùy schema governance của agent #2, nhưng về business plan đây là bảng cần thiết.

## 7.2. Backward-compatible rollout
### Stage 1
- thêm bảng execution
- chưa đổi behavior transaction
- chỉ ghi execution telemetry shadow mode

### Stage 2
- cho `PostgresClient` hỗ trợ shared cursor/connection
- `start_review_run` chạy core transaction thật dưới feature flag

### Stage 3
- tách employee logs thành post-commit checkpointed projection
- bật reconciliation

### Stage 4
- enforce idempotency key / request fingerprint dedup cho workflow route

## 7.3. Xử lý data cũ
Không cần backfill toàn bộ review sessions cũ thành execution đầy đủ.
Chỉ cần:
- execution ledger áp dụng cho run mới
- optional script tạo execution records “historical/imported” cho review gần đây nếu cần observability

## 7.4. Safe fallback khi rollout lỗi
Nếu phát hiện lỗi trong transactional mode:
- tắt feature flag
- quay về orchestration cũ
- vẫn giữ execution telemetry shadow để chẩn đoán

---

## 8. Telemetry cần thêm

## 8.1. Structured logs
Mỗi run cần log các event:
- `workflow_run_started`
- `workflow_policy_evaluated`
- `workflow_core_tx_started`
- `workflow_core_tx_committed`
- `workflow_core_tx_failed`
- `workflow_projection_employee_logs_started`
- `workflow_projection_employee_logs_completed`
- `workflow_projection_employee_logs_failed`
- `workflow_run_completed`
- `workflow_run_reconciliation_required`

Field chung:
- `workflow_run_id`
- `idempotency_key`
- `review_code`
- `review_id`
- `project_code`
- `lead_agent_code`
- `current_step`
- `attempt`
- `duration_ms`
- `error_type`
- `error_message`

## 8.2. Metrics
Counters:
- workflow starts / completes / fails
- core transaction rollback count
- projection degraded count
- replay/resume count
- duplicate request dedup count

Histograms:
- total run duration
- core transaction duration
- employee log projection duration
- time-to-reconciliation

Gauges:
- executions in progress
- executions reconciliation_required
- stale executions

## 8.3. API-visible observability
Response nên có:
- `workflow_run_id`
- `persistence_status`
- `reconciliation_required`
- `completed_checkpoints`

Health/readiness agent khác có thể tiêu thụ:
- số run bị reconciliation_required
- stale in-progress workflow count
- last successful committed workflow timestamp

---

## 9. Kế hoạch triển khai theo tuần / phase

## Phase 0 - 3 đến 5 ngày: phân tích và khóa contract
Mục tiêu:
- chốt taxonomy step
- chốt boundary core vs projection
- chốt execution status model
- align với agent schema sync và integration verification

Việc chính:
- map tất cả methods của `PostgresClient` nào cần cursor injection
- chốt idempotency key derivation
- chốt response additive fields
- chốt feature flags

Deliverables:
- ADR ngắn cho transaction boundary
- mapping step idempotent / non-idempotent
- checklist migration

Acceptance:
- mọi bên thống nhất core aggregate = dossier review; employee logs = post-commit projection

## Phase 1 - Tuần 1: shadow execution ledger
Mục tiêu:
- có visibility mà chưa đổi semantics ghi dữ liệu

Việc chính:
- thêm execution table/model
- `start_review_run` ghi execution start/end/fail ở shadow mode
- thêm structured logs + counters cơ bản
- derive `workflow_run_id`, `idempotency_key`, `request_fingerprint`

Acceptance:
- mỗi run mới đều có execution record
- failure giữa chừng ghi rõ step cuối cùng
- chưa thay đổi API/behavior nghiệp vụ hiện tại

Rollback:
- có thể ngừng ghi execution nếu gây lỗi; không ảnh hưởng review persistence cũ

## Phase 2 - Tuần 2: transaction-scoped Postgres operations
Mục tiêu:
- chuẩn bị hạ tầng để orchestration có thể ôm transaction

Việc chính:
- thêm optional `connection`/`cursor` cho methods liên quan
- đảm bảo behavior cũ còn chạy nếu không truyền cursor
- bổ sung unit/integration tests cho từng method trong và ngoài transaction scope

Acceptance:
- cùng một chuỗi session/finding/assignment/... có thể chạy trong shared transaction
- code paths cũ vẫn pass test hiện có

Rollback:
- giữ backward compatibility; chỉ không dùng transactional path

## Phase 3 - Tuần 3: core workflow transaction
Mục tiêu:
- eliminate partial writes trong review aggregate

Việc chính:
- refactor `start_review_run`:
  - precompute policy / summaries ngoài transaction
  - mở một DB transaction
  - persist session/findings/assignments/submissions/verifications/close trong transaction
  - update execution checkpoint trong transaction hoặc ngay trước/sau commit
- thêm failure injection tests

Acceptance:
- lỗi ở bất kỳ bước core nào => không còn session/finding/assignment orphan từ run đó
- retry cùng idempotency key không tạo duplicate review core data

Rollback:
- feature flag tắt transactional orchestration, quay về old path

## Phase 4 - Tuần 4: tách employee logs thành post-commit projection có checkpoint
Mục tiêu:
- giảm transaction scope và có degraded-safe post-commit behavior

Việc chính:
- chuyển `_persist_employee_logs` thành projection step sau commit
- execution ledger ghi checkpoint projection
- thêm reconciliation runner/manual replay path theo `workflow_run_id`
- response trả rõ `projection_degraded` nếu log step fail

Acceptance:
- employee log fail không làm mất core review đã commit
- replay projection không tạo duplicate actions ngoài dedup contract

Rollback:
- tạm chuyển employee logs chạy synchronous best-effort như cũ nhưng vẫn ngoài core tx

## Phase 5 - Tuần 5: idempotency enforcement + reconciliation operations
Mục tiêu:
- hệ thống chịu retry/crash tốt

Việc chính:
- enforce dedup theo `idempotency_key`
- stale execution recovery policy
- tooling/admin script để replay execution hoặc chỉ replay employee logs
- dashboard / readiness surfacing cho reconciliation backlog

Acceptance:
- timeout/client retry không tạo duplicate workflow core data
- stale in-progress run có thể đánh dấu failed hoặc resumed có kiểm soát
- đội vận hành có cách xử lý execution bị kẹt

---

## 10. Test strategy

## 10.1. Unit tests cho orchestration semantics
Bổ sung test quanh `WorkflowPersistenceService` với fake client/failure injection:

1. fail tại `create_dossier_review_finding` đầu tiên  
   - kỳ vọng: không commit gì nếu đang ở core transaction mode

2. fail tại assignment của finding thứ 2  
   - kỳ vọng: finding/session trước đó cũng rollback

3. fail tại submit/verify  
   - kỳ vọng: không để lại finding resolved một phần của cùng run

4. fail tại close  
   - kỳ vọng: toàn bộ core rollback hoặc review vẫn open nhưng chỉ nếu explicit checkpoint design cho phép; target nên rollback toàn core

5. fail tại `_persist_employee_logs`  
   - kỳ vọng: core review vẫn committed, response/report execution = `projection_degraded`

6. retry cùng `idempotency_key`  
   - kỳ vọng: không tạo review/session trùng

## 10.2. Integration tests với Postgres thật
Cần test mức DB thật, không chỉ fake:
- chạy transaction rollback thật
- verify row counts ở:
  - `dossier_review_sessions`
  - `dossier_review_findings`
  - `dossier_review_assignments`
  - `dossier_review_submissions`
  - `dossier_review_actions`
  - `employee_work_log_sessions`
  - `employee_work_log_actions`
  - `workflow_run_executions`

Test cases:
- happy path full auto-close
- policy block => open/manual follow-up path
- injected DB exception giữa core transaction
- employee log projection fail after commit
- replay execution / replay projection

## 10.3. Contract tests cho API response
Đảm bảo additive-only:
- field cũ vẫn còn
- field mới xuất hiện nhưng không làm vỡ clients cũ
- `automation_status` và `persistence_status` không bị dùng lẫn nghĩa

## 10.4. Fault-injection tests
Nên thêm fake hooks hoặc monkeypatch cho từng step:
- before core commit
- after core commit before projection
- during log append action N
- during fetch refresh

## 10.5. Recovery tests
- run bị fail ở projection => replay projection thành công
- run duplicate request => return same run/review
- stale in-progress => recovery policy hoạt động đúng

---

## 11. Acceptance criteria

## 11.1. Functional
- Không còn partial state trong review core aggregate khi lỗi xảy ra giữa session/finding/assignment/submission/verify/close.
- Một `start_review_run` có thể retry an toàn với cùng idempotency key mà không nhân bản review core data.
- Employee logs nếu lỗi không làm hỏng core review đã commit và được đánh dấu reconciliation rõ ràng.
- Response phân biệt rõ business automation status và persistence integrity status.

## 11.2. Observability
- Mỗi workflow run có execution record persisted.
- Có thể truy ra step cuối cùng, lỗi cuối cùng, số lần retry và trạng thái reconciliation.
- Có log/metrics cho core commit, rollback, projection failure, replay.

## 11.3. Verification
- Có integration tests chứng minh rollback transaction thật trên Postgres.
- Có fault-injection tests cho ít nhất 4 điểm fail giữa workflow.
- Có test retry/idempotency cho cùng request.

## 11.4. Rollout safety
- Có feature flag để bật/tắt transactional orchestration.
- Có rollback plan không phá dữ liệu đã tồn tại.
- Không phá API hiện tại; thay đổi response là additive.

---

## 12. Anti-pattern cần tránh

- Gói toàn bộ cả employee logs và enrichment vào một super-transaction dài.
- Rely vào “fetch lại thấy gần đúng” thay cho execution ledger.
- Retry mù với các step create non-idempotent.
- Dùng `review_code` timestamp như khóa dedup duy nhất.
- Che projection failure dưới cùng `automation_status=completed` mà không có `persistence_status`.
- Compensation bằng cách auto-close/auto-waive record đã ghi nhầm thay vì rollback core transaction.

---

## 13. Phụ thuộc với 4 hướng công việc còn lại

### Với schema sync (#2)
Execution ledger table, idempotency indexes, và có thể các unique constraints cho dedup cần được đưa vào governance schema chính thức.

### Với degraded observability (#3)
`persistence_status`, `reconciliation_required`, source health cho projection failure nên dùng chung taxonomy degraded-state toàn hệ thống.

### Với shared review projection (#4)
Khi review-derived semantics được gom về một projection chung, employee logs/projections nên trở thành consumer sau commit, không còn là phần core transaction.

### Với integration verification (#5)
Flow transaction hardening này phải có priority cao trong integration test matrix vì nó là nền của nhiều service khác.

---

## 14. Kết luận

Cách xử lý triệt để cho DSCons không phải chỉ “bọc thêm try/except”, mà là:

1. xác định rõ core aggregate của workflow review  
2. gom core writes vào một transaction PostgreSQL duy nhất  
3. thêm workflow execution ledger để checkpoint/resume/idempotency  
4. tách employee logs thành post-commit projection có retry/reconciliation  
5. bổ sung telemetry và integration tests chứng minh rollback thật

Nếu làm theo lộ trình trên, DSCons sẽ chuyển từ trạng thái “workflow orchestration commit từng mảnh” sang “core committed atomically, projections eventual nhưng quan sát được và recoverable”.