Design baseline · 28 SEP 2026 · 11 / 23
TDD · UC-01 Cont/Seal
Current runtime: một task OPS đọc album và một child task CUSTOMER_SERVICE gửi kết quả trong cùng legacy workflow instance. Target: Process Definition chứa Workflow Cont/Seal nhiều task do Field Operations sở hữu, cùng Workflow gửi kết quả do Customer Service sở hữu.
Luồng Cont/Seal hiện tại
Hiện tại chỉ có một task đọc/xử lý album; task gửi kết quả là một child task riêng chứ không phải bước thứ hai của AI đọc ảnh. Đây là current vertical slice, chưa theo hierarchy mới: mỗi inbound event đủ điều kiện tạo một legacy workflow instance với một OPS task/image job; image processor chạy code chuyên biệt, sau đó tạo CUSTOMER_SERVICE child task trong cùng instance để gửi report. Vì một instance đang chứa task của hai Role, đây là cấu trúc chuyển tiếp và không tuân thủ nguyên tắc target mỗi Workflow có đúng một Role owner. Role/task chưa được cấu hình qua UI.
Mapping sang kiến trúc đích
Trong Process Definition logistics shipment, Cont/Seal sẽ là một Workflow con có owner Role FIELD_OPERATIONS. Workflow này chứa các task đọc evidence, kiểm tra ISO/seal và xử lý review; task graph được phép fan-out/fan-in. Nếu cần xác nhận booking thuộc trách nhiệm Documentation, handoff sang Workflow con của Role đó. Gửi kết quả cho khách/nhà xe là Workflow con khác, owner CUSTOMER_SERVICE, được kích hoạt theo transition đã khai báo. Tên Process Definition và role catalog cần được owner nghiệp vụ chốt trước khi coi là contract.
UC-02–05 nằm ngoài vertical slice Cont/Seal hiện tại.
Envelope và version pinning
{
"schemaVersion": "2.0",
"taskId": "task_*",
"taskType": "RECEIVE_CONTAINER_SEAL",
"role": {"code": "OPS", "version": 1},
"flow": {"id": "wf_*", "version": 4, "instanceId": "wfi_*"},
"source": {"eventId": "evt_*", "channel": "zalo", "sessionId": "sess_*", "groupId": "group_*"},
"evidence": [{"type": "image", "sourceEventId": "evt_*"}],
"skill": {"code": "extract_container_evidence", "version": 1}
}
Tenant lấy từ event/session đã xác thực, không lấy từ payload. Legacy workflow instance lưu definition snapshot. Khi OPS hoàn tất, child task SEND_CONTAINER_SEAL_RESULT pin CUSTOMER_SERVICE role/skill và destination snapshot nhưng vẫn nằm trong cùng instance. Đây là hành vi hiện tại; target sẽ tách gửi kết quả thành Workflow con riêng do CUSTOMER_SERVICE sở hữu, được Process Orchestrator handoff trong cùng Process Instance. Job đang chạy không đọc destination từ config mới nhất.
State và lỗi
event: STORED → DISPATCHED | IGNORED
OPS task: PENDING → RUNNING → SUCCEEDED | WAITING_REVIEW | FAILED
flow: RUNNING → WAITING_DELIVERY → SUCCEEDED | UNKNOWN
result task: WAITING_DELIVERY → SUCCEEDED | UNKNOWN
UNKNOWN không tự gửi lại; cần đối soát receipt.
- Flow tắt, ảnh không thuộc group nguồn hoặc event là text: lưu event, không tạo Cont/Seal task.
- Inbound replay: trả cùng dispatch/task; không tạo instance hay outbox trùng.
- Flow đổi sau khi task tạo: task tiếp tục dùng role/skill/flow/destination snapshot cũ.
- Model output sai schema, confidence thấp, cont/seal sai định dạng hoặc provider lỗi: cần review; không ghi fact đã xác minh.
- Sheet chưa cấu hình không chặn US-01. Sheet mismatch/lỗi đã cấu hình thì result cần review.
- Gateway receipt accepted → SENT; timeout hoặc response mơ hồ → UNKNOWN, không tuyên bố đã gửi.
Test matrix theo acceptance
| ID | Given / When | Then |
|---|---|---|
| UC01-T01 · AC1/5 | OPS mở Workflows với tenant/session được phép | Đúng config; save tạo revision; optimistic conflict không ghi đè. |
| UC01-T02 · AC2/3/4 | Tải Zalo inventory; chọn nhiều nhóm nhà xe và result | Tên thật, giữ channel/session/group ID; không có phân loại theo từng nhóm; reject ID ngoài inventory, trùng hoặc source=result. |
| UC01-T03 · AC5 | Bật flow thiếu source/result hoặc graph sai | Lỗi cụ thể; không tạo revision. |
| UC01-T04 · AC6 | Một album từ group đã chọn | Một OPS task/flow instance; attachment theo thứ tự; ảnh sai nguồn không gọi vision. |
| UC01-T05 · AC6 | Vision trả hợp lệ, sai ISO, thiếu cont, confidence thấp hoặc outage | Quality gate quyết VERIFIED/NEEDS_REVIEW; outage không làm mất ảnh hoặc tự xác minh. |
| UC01-T06 · AC7 | OPS đang xử lý khi admin đổi result group | Child task gửi tới group trong snapshot cũ; report mở đầu bằng tên nhóm nguồn, kèm kết quả và media nguồn, không gửi ghi chú trích xuất. |
| UC01-T07 · AC7/8 | Gateway trả receipt hoặc timeout | Receipt accepted → SENT; timeout → UNKNOWN; không tạo vòng lặp inbound. |
| UC01-T08 · AC8 | Inbound replay, worker restart, duplicate outbox claim | Unique event/task/command ngăn gửi trùng; lease hết hạn không tự retry side effect mơ hồ. |
| UC01-T09 · isolation | Hai tenant có ID hội thoại giống nhau | Không đọc workflow, evidence, task hoặc destination của tenant kia. |
Definition of done
- Test config/API/validator xanh; migration chạy trên DB mới và DB đã có V1–V21.
- Kiểm tra task có role/version, task type, skill/version, workflow snapshot; result tạo CUSTOMER_SERVICE child task và outbox đúng destination.
- Kiểm tra ảnh từ group chưa cấu hình không sinh task; đổi config trong lúc chạy không đổi destination.
- Operational acceptance cần session Zalo kết nối, quyền đọc/gửi group, model credential, media URL hợp lệ và gateway receipt; unit test đơn lẻ chưa đủ để xác nhận “dùng được”.
Historical verification record · 25 Sep 2026
| Check | Result | Coverage boundary |
|---|---|---|
bun run app:typecheck | PASS | Dashboard TypeScript, including workflow BFF. |
bun run app:test | PASS · 39/39 | Current workflow config, role-assignment removal, toast layout, and 9Router BFF tests; not live provider inference. |
bun test gateway/tests/*.test.ts | PASS · 31/31 · 24 Sep | Session/QR, tenant-scoped secrets, image album, inbound/outbound contract mocks; not rerun for this UI/core change and not a real Zalo room. |
mvn test | PASS · 106/106 | Current core APIs, Cont/Seal report formatting and carrier-only source filtering, graph validator, Sheet MCP client and vision adapter. |
| PostgreSQL V1–V22 migration check | PASS | Disposable database with one existing enabled Cont/Seal workflow and queued image; V22 backfilled ASSIGN → OPS / RECEIVE_CONTAINER_SEAL and linked its flow version. |
| Inbound runtime smoke | PASS | Disposable DB + Quarkus: configured source album → ASSIGN/OPS task; replay → duplicate; unconfigured group → IGNORE. Image processing was disabled in this smoke, so it made no model or outbound call. |
| Live image preview · no side effect | PASS | Previewed one retained photo from the configured Zalo source group through the selected vision route; detected both container and seal, confidence 0.98, quality status PASSES_CHECKS. |
| UC-01 full-flow integration · disposable DB | PASS | Same real image source → ASSIGN / OPS / RECEIVE_CONTAINER_SEAL → image job DONE → CUSTOMER_SERVICE child task → report SENT to a mock gateway receipt → child and flow SUCCEEDED. Destination matched the pinned result group. No real Zalo send. |
The selected Zalo session is connected and its workflow is enabled with a source group and result group. The real model has read a retained group photo, and the complete delivery state machine has passed against a mock gateway. A newly submitted album is still the final live-room check for the post-V22 deployment; no test report was sent to the real result group.