48 KiB
title, description, version, status, created, updated, owners
| title | description | version | status | created | updated | owners | ||
|---|---|---|---|---|---|---|---|---|
| Lead Scoring Customer Pilot Specification | Đặc tả buildable và cross-service cho Lead Scoring Customer Pilot trên AI Platform, DotB-Omni, DotB EMS và AIHub. | 1.0.0 | approved | 2026-08-26 | 2026-08-26 |
|
Lead Scoring Customer Pilot Specification
Khi được phê duyệt, tài liệu này là nguồn sự thật cho product behavior, scope, invariants và acceptance boundary của Lead Scoring Customer Pilot. Executable contracts là nguồn sự thật cho exact wire format.
stack.mdlà technology decision ledger có bằng chứng, không override tài liệu này.
Problem Statement
DotB cần chấm mức độ tiềm năng của các Lead đang tồn tại trong DotB EMS bằng cả quy tắc nghiệp vụ và khả năng đọc hiểu ngữ cảnh hội thoại của AI. Hiện dữ liệu CRM, hội thoại, quyền người dùng, model access và quota nằm ở các hệ thống khác nhau; nếu tích hợp trực tiếp hoặc để mỗi ứng dụng tự định nghĩa scoring contract, hệ thống sẽ tạo nhiều nguồn sự thật, khó audit, dễ làm rò credential và khó phục hồi khi worker, AIHub hoặc EMS gặp lỗi.
Customer Pilot cần giải quyết bài toán này cho khách hàng thật trên tenant allowlist mà vẫn giữ các boundary sau:
- DotB EMS tiếp tục là nguồn dữ liệu CRM Lead.
- DotB-Omni chịu trách nhiệm identity resolution, conversation enrichment, giao diện cấu hình/HITL và EMS writeback.
- Lead Scoring service chịu trách nhiệm scoring config, scoring run, rule/AI scoring, review lifecycle và domain events.
- AIHub chịu trách nhiệm V-Key validation, model access, quota, credit và LLM audit.
- Raw tenant V-Key không trở thành dữ liệu persistent của Lead Scoring.
- Kết quả cũ, event trùng, AI failure hoặc Marker Block malformed không được âm thầm phá dữ liệu mới hơn hay ghi đè nội dung người dùng trên EMS.
Pilot cũng cần tạo ra một executable contract đủ ổn định để AI Platform và DotB-Omni phát triển song song, thay vì phụ thuộc vào prose hoặc payload tự suy diễn.
Solution
Xây một Lead Scoring service Python-first trên AI Platform với API caller-agnostic, durable scoring runs, bounded rule/AI pipelines, Generic Review API và Transactional Outbox. DotB-Omni tạo Canonical Lead Snapshot, gửi tenant V-Key qua header trong request, nhận domain events từ registered subscriber boundary và ghi kết quả hiện hành vào một Marker Block được quản lý an toàn trong EMS Lead description.
Customer Pilot có hai production initiation paths:
- Người dùng chủ động chấm điểm trên Omni.
- Captain chủ động gọi
create_and_score_ems_leadtrong một orchestration flow được phép.
AI Platform Dev Console là caller nội bộ phục vụ test và integration verification với test V-Key riêng. Generic automatic input-event trigger chưa có use case và policy đủ rõ nên được defer sau Pilot. Điều này không ảnh hưởng completion events từ Lead Scoring sang Omni; completion delivery vẫn là phần bắt buộc của Pilot.
Lead Scoring persist run trước khi trả HTTP 202 Accepted, giữ V-Key trong RAM của execution attempt, forward V-Key sang AIHub, tính rule/AI score và chuyển run tới completed, review_required hoặc failed. Khi AIHub thất bại, policy mặc định là rules_only_review: dùng rule score, đánh dấu degraded và bắt buộc HITL. Review actions gồm Approve, numeric Override và Re-evaluate; không có action hoặc state Reject riêng.
Exact API, schema, error codes và examples được phát hành qua Shared Executable Contract Pack do AI Platform sở hữu và DotB-Omni review/consume. Mỗi repository có implementation spec riêng nhưng không được định nghĩa một wire contract cạnh tranh.
User Stories
- Là một tenant administrator, tôi muốn Lead Scoring chỉ xuất hiện khi EMS integration đang active, tenant thuộc Pilot allowlist và feature được bật, để tính năng không được sử dụng ngoài phạm vi thử nghiệm.
- Là một tenant administrator, tôi muốn có kill switch an toàn, để có thể dừng initiation mới mà không làm hỏng EMS Lead hoặc conversation data.
- Là một tenant administrator, tôi muốn cấu hình AIHub tenant credential qua một write-only backend flow, để raw V-Key không bị trả lại frontend sau khi lưu.
- Là một tenant administrator, tôi muốn rotate hoặc clear AIHub tenant credential, để credential lifecycle không phụ thuộc vào deploy code.
- Là một tenant administrator, tôi muốn quản lý scoring configuration trên Omni, để điều chỉnh rules, weights, score bands, HITL policy và AI effort cho tenant.
- Là một tenant administrator, tôi muốn scoring configuration được lưu thành immutable versions tại Lead Scoring service, để mọi run có thể truy nguyên đúng config đã sử dụng.
- Là một tenant administrator, tôi muốn review mọi run trong account, để có thể xử lý run unassigned, degraded hoặc có writeback failure.
- Là một caller ứng dụng đã xác thực, tôi muốn chủ động chấm điểm Contact/EMS Lead theo quyền người dùng mà ứng dụng đã kiểm tra, để ưu tiên hành động tiếp theo bằng dữ liệu mới nhất.
- Là một caller ứng dụng đã xác thực, tôi muốn thấy run đang pending, running, completed, review-required hoặc failed, để không bấm chấm điểm lặp lại vì thiếu feedback.
- Là một caller ứng dụng đã xác thực, tôi muốn Approve kết quả theo quyền người dùng của ứng dụng, để xác nhận điểm hệ thống.
- Là một caller ứng dụng đã xác thực, tôi muốn Override điểm bằng một số từ 0 đến 100, để phản ánh thông tin nghiệp vụ mà scoring chưa nhận biết đầy đủ.
- Là một caller ứng dụng đã xác thực, tôi muốn dùng điểm 0 cho spam hoặc lead rác mà không cần một state Reject riêng, để domain state không phình to không cần thiết.
- Là một caller ứng dụng đã xác thực, tôi muốn yêu cầu Re-evaluate, để hệ thống tạo snapshot mới và child run thay vì sửa kết quả cũ.
- Là một caller ứng dụng, tôi muốn quyền review được kiểm tra ở hệ thống sở hữu phiên người dùng, để LS không phải đọc assignment hoặc mô hình quyền riêng của caller.
- Là một caller ứng dụng, tôi muốn LS ghi nhận đúng actor và ghi chú review, để quyết định vẫn có audit dù authorization nằm ở caller.
- Là một caller ứng dụng, tôi muốn run không có trigger conversation hoặc conversation chưa assign vẫn có thể review khi quyền caller cho phép, để không tạo rule authorization trùng lặp ở LS.
- Là một Omni user, tôi muốn permission được backend Omni enforce chứ không chỉ ẩn button, để API không thể bị gọi vượt quyền từ client khác.
- Là một Omni user, tôi muốn Contact có direct EMS mapping được dùng ngay, để không lookup và chọn nhầm Lead khác.
- Là một Omni user, tôi muốn khi chưa có direct mapping, Omni tìm EMS candidates theo normalized phone, để identity resolution có điểm bắt đầu deterministic.
- Là một Omni user, tôi muốn closed Leads bị loại khi vẫn còn active candidate, để scoring áp dụng cho Lead đang được xử lý.
- Là một Omni user, tôi muốn email và tên chỉ xếp hạng trong phone candidate set, để một Lead chỉ khớp email không thay thế phone candidate ngoài policy.
- Là một Omni user, tôi muốn
date_modifiedlàm tie-breaker deterministic, để cùng một input luôn chọn cùng EMS Lead. - Là một reviewer, tôi muốn phone-only match được ghi
match_quality=phone_only, để biết identity evidence yếu hơn direct hoặc secondary match. - Là một Omni user, tôi muốn không có candidate thì không tạo scoring run, để LS không chấm một subject không xác định.
- Là một Omni user, tôi muốn resolved EMS Lead được lưu lại trên Contact và gửi làm
subject_ref, để các lần sau dùng cùng canonical identity. - Là một Lead Scoring caller, tôi muốn gửi Canonical Snapshot có version và field constraints rõ ràng, để provider có thể validate deterministic.
- Là một privacy owner, tôi muốn snapshot chỉ chứa conversation evidence trong phạm vi đã chốt, để AI Platform không trở thành kho conversation thứ hai không giới hạn.
- Là một tenant, tôi muốn snapshot giới hạn tối đa 10 conversations trong 90 ngày, để kiểm soát privacy và context size.
- Là một tenant, tôi muốn evidence bị giới hạn ở 50 messages hoặc 20.000 ký tự, để chi phí và latency có hard cap.
- Là một reviewer, tôi muốn evidence truncation loại messages cũ nhất trước và ghi metadata, để biết kết quả được tạo từ phạm vi dữ liệu nào.
- Là một Omni user, tôi muốn manual scoring đóng băng snapshot tại thời điểm submit, để messages đến sau không âm thầm thay đổi run đang xử lý.
- Là Captain, tôi muốn upsert EMS Lead rồi tạo scoring run qua cùng API với manual flow, để không có scoring logic riêng trong Captain.
- Là Captain, tôi muốn EMS Lead vẫn được giữ nếu scoring initiation thất bại, để CRM data không bị rollback vì AI service lỗi.
- Là Captain, tôi muốn raw V-Key không bao giờ được đưa vào prompt, tool result hoặc LLM-visible context, để credential không bị model nhìn thấy.
- Là một internal developer, tôi muốn dùng AI Platform Dev Console để gửi fixtures qua cùng Scoring Run API, để kiểm tra contract mà không phụ thuộc Omni UI.
- Là một internal developer, tôi muốn Dev Console dùng test V-Key riêng ở backend, để test không lộ hoặc tiêu quota production ngoài ý muốn.
- Là một Lead Scoring caller, tôi muốn request hợp lệ được persist thành run trước khi nhận
HTTP 202, để accepted request không biến mất im lặng. - Là một Lead Scoring caller, tôi muốn LS tự sinh
run_idvà trảstatus_url, để caller không phải sở hữu run identity. - Là một Lead Scoring caller, tôi muốn cùng tenant, idempotency key và payload trả lại run hiện có, để duplicate submission không tạo run mới.
- Là một Lead Scoring caller, tôi muốn cùng idempotency key nhưng payload khác bị từ chối bằng
HTTP 409, để key reuse không che giấu hai hành động khác nhau. - Là một operator, tôi muốn run bị mất in-memory task/V-Key do worker interruption chuyển thành failed/retryable, để không có run kẹt vô hạn.
- Là một caller, tôi muốn tạo request mới với idempotency key mới sau run failed, để retry là một hành động scoring mới có audit rõ ràng.
- Là một tenant, tôi muốn rule score và AI score được kết hợp bằng versioned weights, để kết quả vừa minh bạch vừa tận dụng ngữ cảnh hội thoại.
- Là một tenant, tôi muốn
lightcung cấp một bounded AI pass, để ưu tiên tốc độ và chi phí. - Là một tenant, tôi muốn
mediumlà mặc định và tách evidence extraction khỏi scoring, để AI feature có giá trị hơn một score prompt đơn lẻ. - Là một tenant, tôi muốn
heavylà opt-in và thêm bounded critique/cross-check, để các trường hợp cần phân tích kỹ có thể dùng thêm reasoning. - Là một operator, tôi muốn mọi AI effort dùng chung output contract, để các thành phần downstream không cần biết pipeline nội bộ.
- Là một security owner, tôi muốn AI pipelines không dùng Internet, autonomous tools hoặc unbounded loops, để runtime và dữ liệu nằm trong boundary kiểm soát.
- Là một reviewer, tôi muốn partial AI result bị discard nếu bất kỳ pass nào thất bại, để không review một kết quả chắp vá.
- Là một tenant, tôi muốn AIHub failure mặc định tạo
rules_only_review, để hệ thống vẫn có điểm tạm nhưng không auto-approve khi AI bị degraded. - Là một tenant có policy nghiêm ngặt, tôi muốn có thể chọn
fail_runkhi AIHub lỗi, để không dùng rule-only result nếu nghiệp vụ không chấp nhận. - Là một operator, tôi muốn LS không auto-retry AIHub timeout/network/5xx trong Pilot, để hạn chế duplicate provider invocation và quota.
- Là một operator, tôi muốn
run_idđược truyền quax-request-idtới AIHub, để correlate logs mà không lưu thêm credential hoặc prompt. - Là một product owner, tôi chấp nhận AIHub invocation có semantics at-least-once trong crash window của Pilot, để không phải xây exactly-once subsystem trước khi có nhu cầu thực tế.
- Là một reviewer, tôi muốn confidence quyết định auto-complete hay review-required, để score không bị dùng nhầm làm review threshold.
- Là một reviewer, tôi muốn default confidence threshold là 0,80 nhưng policy có version, để thay đổi sau này có thể audit.
- Là một reviewer, tôi muốn Approve giữ calculated score, để quyết định không làm mất kết quả gốc.
- Là một reviewer, tôi muốn Override giữ cả calculated score và effective score, để audit thấy điểm nào do người sửa.
- Là một reviewer, tôi muốn Re-evaluate tạo child run và supersede run cũ, để lineage được bảo toàn.
- Là một reviewer, tôi muốn concurrent review command dùng
expected_run_versionvà trả409khi stale, để quyết định cũ không ghi đè quyết định mới. - Là một Omni UI user, tôi muốn UI reload khi nhận review conflict và không tự replay command, để người dùng nhìn thấy trạng thái hiện tại trước khi quyết định lại.
- Là một subscribing service, tôi muốn LS ghi event cùng run transition trong một transaction, để committed result không mất completion notification.
- Là một subscribing service, tôi muốn completion delivery là at-least-once và có bounded retry/DLQ, để transient failure không mất event.
- Là một security owner, tôi muốn LS chỉ gửi tới registered subscriber endpoint và không nhận arbitrary callback URL từ create request, để tránh SSRF.
- Là một Omni consumer, tôi muốn event có
run_id,run_created_at, tenant, subject và config version, để có thể xác định latest applicable run. - Là một Omni consumer, tôi muốn stale hoặc same-run duplicate event là no-op và được ack
HTTP 200, để dispatcher không retry vô ích. - Là một Omni consumer, tôi muốn ordering dùng
run_created_atthay vìevent.occurred_at, để run hoàn tất muộn không bị hiểu là run mới hơn. - Là một Omni consumer, tôi muốn hai run khác nhau cùng
run_created_attạo ordering conflict thay vì tự ghi đè, để ambiguity không làm mất kết quả. - Là một EMS user, tôi muốn Omni chỉ thay đúng một Marker Block hợp lệ và giữ nguyên user text ngoài block, để scoring không phá ghi chú CRM.
- Là một EMS user, tôi muốn Omni append Marker Block khi không có marker tag, để Lead lần đầu vẫn nhận kết quả.
- Là một EMS user, tôi muốn orphan marker hoặc nhiều block làm writeback terminal mà không sửa description, để parser không nuốt hoặc ghi đè nội dung người dùng.
- Là một operator, tôi muốn EMS timeout/network/5xx dùng bounded retry còn malformed block không retry, để retry chỉ áp dụng cho lỗi có khả năng tự hồi phục.
- Là một operator, tôi muốn writeback failure không làm scoring run completed trở thành failed, để scoring lifecycle và delivery lifecycle không bị trộn.
- Là một tenant administrator, tôi muốn xóa toàn bộ Marker Block không tự disable writeback, để feature state chỉ thay đổi qua configuration rõ ràng.
- Là một privacy owner, tôi muốn raw conversation, snapshot, direct PII, free-text AI/reviewer và personal derived fields bị xóa sau 90 ngày, để AI Platform giảm dữ liệu nhạy cảm lưu lâu dài.
- Là một privacy owner, tôi muốn chỉ giữ structured audit tối thiểu sau redaction, để vẫn điều tra run mà không giữ một hồ sơ Lead thứ hai.
- Là một privacy owner, tôi muốn structured audit bị purge khi tenant offboard, để dữ liệu không tồn tại vô hạn sau khi quan hệ khách hàng kết thúc.
- Là một product owner, tôi muốn early deletion integration chỉ được xây nếu Omni có reliable deletion/privacy event trong Pilot, để không tạo subsystem dựa trên giả định.
- Là một operator, tôi muốn metrics bao phủ run terminality, latency, AIHub failure, HITL, override, token, outbox và writeback, để Pilot tạo baseline thật.
- Là một product owner, tôi muốn chưa đặt SLO/quality threshold cứng trước baseline, để exit criteria không dựa trên con số suy đoán.
- Là một operator, tôi muốn no silent stuck run/event/writeback, để mọi công việc đều complete, fail, retry, DLQ hoặc alert rõ ràng.
- Là một product owner, tôi muốn Pilot chỉ kết thúc khi retention và kill switch được kiểm chứng, không còn P0/P1 mở và có workload tenant thật đã thỏa thuận, để GA decision dựa trên evidence.
Implementation Decisions
Authority và document hierarchy
- Tài liệu này sở hữu product behavior, scope, cross-service invariants và acceptance boundary của Customer Pilot.
- Shared Executable Contract Pack sở hữu exact request/response/event shapes, enums, field constraints, machine-readable error codes và golden examples.
- Pydantic models là nguồn API/data model phía provider; FastAPI-generated OpenAPI và generated JSON Schemas là derived artifacts, không được hand-edit song song.
- DotB-Omni và AI Platform có implementation spec riêng cho code/module seams của từng repository; chúng không được định nghĩa wire contract cạnh tranh.
stack.mdghi technology status, decisions và trade-offs; nó không thay đổi product scope hoặc approved behavior.- Discovery docs, RFC drafts và Grill Brief vẫn là nguồn context/lý do. Khi một statement discovery cũ mâu thuẫn với spec đã approved, spec thắng; exact wire format vẫn theo released executable contract.
System ownership
- DotB EMS là source of truth cho CRM Lead data.
- DotB-Omni sở hữu Contact-to-EMS identity resolution, Canonical Snapshot construction, caller-side V-Key boundary, customer-facing Settings/HITL UI, completion-event consumption và EMS Marker Block writeback.
- Lead Scoring sở hữu immutable scoring config versions, scoring runs, scoring calculations, review state, audit events và outgoing domain events.
- AIHub sở hữu V-Key semantics, model access/routing, quota, credit, rate limits và LLM audit.
- AI Platform Dev Console là internal test caller; nó không sở hữu customer-facing scoring configuration hoặc HITL UI.
Pilot initiation boundary
- Customer-facing production initiation paths chỉ gồm manual Omni action và Captain tool invocation.
- Captain phải chủ động chọn tool trong một orchestration flow được phép; incoming message tự nó không phải scoring trigger.
- AI Platform Dev Console có thể gọi cùng API bằng fixture/test data và test V-Key riêng, nhưng không được xem là Customer Pilot feature.
- Generic automatic input-event trigger được defer. Completion events từ LS sang Omni vẫn bắt buộc và không phải input trigger.
- LS chưa cung cấp batch API; caller fan-out nếu sau này có use case batch được phê duyệt.
Eligibility và authorization
- Lead Scoring chỉ có thể sử dụng khi EMS integration active, account có
lead_scoringcapability, tenant thuộc Pilot allowlist và global availability/kill-switch cho phép. - Credential readiness và service caller authorization phải được backend kiểm tra; end-user authorization thuộc caller ứng dụng và UI không thay thế backend enforcement của caller.
- Administrator quản lý Settings và AIHub tenant credential; quyền review do caller ứng dụng quyết định.
- Omni chịu trách nhiệm xác thực người dùng cuối, vai trò, assignment và quyền Approve, Override hoặc Re-evaluate trước khi gọi Review API.
reviewer.actor_idlà danh tính do caller gửi để ghi audit.reviewer.rolevàcurrent_assignee_idchỉ là metadata tương thích cho audit/context, không phải bằng chứng phân quyền tại LS.- LS không đọc trigger conversation, assigned agent hoặc reviewer role để cấp hay từ chối quyền review.
- LS chỉ xác thực service caller, operation scope, tenant boundary, run state, idempotency và optimistic concurrency.
- Pilot dùng permission vocabulary hiện có của ứng dụng caller và không mở rộng CustomRole chỉ cho Lead Scoring.
- Nếu caller bỏ qua end-user authorization, đó là lỗi boundary của caller, không phải logic phân quyền mới tại LS.
Canonical Lead Identity
- Canonical subject của một run là một EMS Lead đã tồn tại.
- Direct
dotb_ems_idmapping có ưu tiên cao nhất. - Khi chưa có mapping, Omni tạo candidate set bằng normalized phone lookup.
- Closed candidates bị loại khi còn active candidates.
- Exact email/name chỉ xếp hạng trong phone candidate set.
date_modifiedlà deterministic tie-breaker.- Phone-only result được phép và phải mang
match_quality=phone_only. - Không có candidate thì không tạo run.
- Resolved EMS Lead, kể cả phone-only result, được lưu lại trên Contact và dùng làm
subject_reftheo Policy v1. Omni phải lưu match quality cùng mapping hoặc cung cấp khả năng sửa mapping; Policy v1 không tự re-resolve một direct mapping ở lần scoring sau.
Canonical Snapshot
- Omni tạo snapshot versioned từ EMS Lead profile, scoped conversation evidence, identity context, trigger metadata và HITL policy.
- Snapshot không chứa raw CRM credential hoặc AIHub V-Key.
- Contract hỗ trợ
trigger_only,linked_contact_historyvàmulti_source_history;linked_contact_historylà mặc định. - Omni chịu trách nhiệm structural caps: mọi conversation scope tối đa 10 conversations trong 90 ngày, 50 messages hoặc 20.000 ký tự; chỉ incoming/outgoing messages có content được dùng.
- Khi vượt structural cap, Omni bỏ evidence cũ nhất trước và ghi truncation metadata.
- LS vẫn kiểm tra internal model token budget sau admission và có thể rút gọn evidence thêm trước AIHub; lần rút gọn này phải cập nhật lineage/truncation metadata mà không thay đổi snapshot request gốc đã lưu.
- Hành vi admission khi caller vi phạm structural caps thuộc executable contract; spec không tự khóa HTTP status trước Contract Sprint.
- Create request không chứa
run_id; LS sinh run identity sau khi admission thành công. - Exact required/nullable fields thuộc executable schema, không được Omni hoặc LS suy diễn riêng.
Scoring Run admission và idempotency
- Mọi initiation path dùng chung
POST /v1/scoring-runsvà cùng contract. - Required caller metadata gồm tenant V-Key, application, feature và idempotency key qua headers được định nghĩa trong executable contract.
- LS validate request, persist run
pending, sau đó trảHTTP 202với server-generatedrun_idvàstatus_url. - Idempotency key scoped theo tenant.
- Cùng key và cùng normalized request trả run hiện có.
- Cùng key nhưng normalized request khác trả
HTTP 409với machine-readable mismatch error. - Run đã failed muốn chấm lại là action mới với idempotency key mới.
V-Key lifecycle và execution durability
- Caller sở hữu/provide V-Key phù hợp; AIHub tenant credential là domain concept riêng và không được mặc định đồng nhất với Captain/OpenAI account key. LS chỉ giữ raw V-Key trong RAM của current attempt và dùng transport chuẩn
Authorization: Bearer <V-Key>khi gọi AIHub. AIHub có thể hỗ trợ credential header khác, nhưng chúng không thuộc Lead Scoring contract và consumer không được tự chọn transport. - Raw V-Key không được lưu vào DB, persistent queue, cache, checkpoint, logs, trace payload, error metadata, crash dump hoặc browser code.
- Run được persist trước
202, nhưng execution task/V-Key vẫn là in-memory boundary của Pilot. - Worker/process interruption làm mất task/V-Key phải chuyển run không còn khả năng hoàn tất thành
failed,error_code=worker_interrupted,retryable=true. - Recovery/stuck-run mechanism phải bảo đảm không có run tồn tại vô hạn ở
pendinghoặcrunningmà không terminalize/alert. - Exact worker topology và database engine là implementation decisions, nhưng phải thỏa các invariants trên.
Service-to-service caller authentication
- Customer Pilot chỉ cho phép một tập machine caller được registered/allowlisted. Omni production và AI Platform Dev Console là hai caller identities riêng; mỗi caller và mỗi environment dùng một opaque service token riêng.
- Caller gửi service token qua chuẩn
Authorization: Bearer <service-token>. Lead Scoring nạp allowlist từ deployment secret/configuration ngoài repository, map token với caller identity và allowed operation/scope, rồi xác thực trước khi xử lý payload hoặc tạo run. X-DotB-AppvàX-DotB-Featurelà metadata/telemetry; chúng phải khớp caller đã authenticate nhưng không tự chứng minh caller identity.X-Tenant-VKeylà tenant credential cho Lead Scoring → AIHub, không phải service identity cho Omni hoặc Dev Console.- Service token được tạo bằng cryptographically secure random generation, provision ở backend deployment secret/ENV, không lưu trong source, browser, account data, integration settings, logs hoặc traces. Omni dùng system-level configuration; không lưu token theo từng tenant.
- Mất hoặc sai service token phải bị reject trước admission; authenticated caller nhưng sai scope hoặc tenant boundary cũng phải bị reject. Exact error taxonomy thuộc Contract Pack.
- Pilot không dựng mới JWT issuer/OAuth server, mTLS, gateway hoặc service mesh cho caller authentication. Rotation/revocation và secret delivery phải được kiểm thử trong deployment/operational runbook.
Scoring configuration và calculation
- LS là source of truth cho immutable scoring config versions; Omni là editor/UI client.
- Rule score, AI score và final score nằm trong khoảng 0–100.
- Final score là weighted combination của rule và AI components, với weights có tổng bằng 1.
- Config lưu score bands, rules/weights, AI effort, failure policy và HITL policy/version cần thiết để tái hiện decision context.
- Legacy rules/prompts chỉ là candidate inputs; trước production use cần product validation và deterministic fixtures.
- Exact default rules/weights ngoài những gì released config xác định không được hard-code như universal domain truth.
Bounded AI pipelines
light,mediumvàheavylà orchestration semantics do LS sở hữu, không phải capability native mà AIHub phải cung cấp.lightchạy một bounded AI analysis pass.mediumlà mặc định và dùng bounded evidence-extraction rồi scoring passes.heavylà opt-in và bổ sung bounded critique/cross-check trước final scoring.- Ba effort profiles dùng chung output contract.
- Không profile nào dùng autonomous tools, Internet access hoặc unbounded loop trong Pilot.
- Nếu một pass thất bại, toàn bộ partial AI result bị discard và
on_ai_failuređược áp dụng. - LS sở hữu số pass, model mapping, token caps và timeout budgets cho từng effort profile; các giá trị này được cấu hình/kiểm chứng trong provider implementation và Contract Sprint mà không thay đổi product semantics của ba profiles.
AIHub interaction và failure policy
- LS gửi
run_idquax-request-idchỉ để correlation. Contract không cam kết AIHub trả header này về response, forward nó tới provider hoặc dùng nó cho idempotency. dotb_appvàdotb_featuređã persist lúc admission được map sang AIHub dưới dạngx-bf-dim-appvàx-bf-dim-featuređể AIHub ghi nhận telemetry/log dimensions. Lead Scoring không forwardX-DotB-*sang AIHub. Các dimension header này chỉ phục vụ log attribution, không phải auth hay quota.run_idvàx-request-idkhông phải AIHub idempotency key; replay có thể tạo invocation và quota charge mới.- LS không auto-retry AIHub invocation trong Pilot. AIHub có thể có retry/provider-fallback nội bộ theo gateway configuration; Contract Sprint phải pin Pilot profile về max attempts, timeout, fallback chain và quota semantics thay vì dựa vào gateway defaults.
- Remote AIHub invocation có semantics at-least-once; duplicate provider invocation/quota trong crash window được chấp nhận và phải được ghi như Pilot limitation.
- Không tuyên bố exactly-once end-to-end.
rules_only_reviewlà failure default:final_score=rule_base_score,degraded=true, scoring mode ghi rules-only fallback và run bắt buộcreview_required.fail_runlà config alternative và tạo failed run với machine-readable AIHub failure outcome.- AIHub quota, budget, rate-limit, authentication và provider failures phải được normalize theo executable taxonomy có
error_code, HTTP status,retryablevàfallback_allowed; consumer không được parse reason text của AIHub làm stable contract.
Scoring Run và review lifecycle
- Primary states gồm
pending,running,completed,review_required,failed;supersededbiểu diễn run cũ khi Re-evaluate tạo child run. - Calculated data và config lineage của run không bị sửa để tạo lịch sử khác; retention redaction được phép xóa sensitive evidence theo policy mà không sửa structured audit.
- Approve giữ calculated score làm effective score.
- Override nhận numeric score 0–100 và giữ cả calculated/effective values.
- Với Re-evaluate, Omni thu thập snapshot mới tại thời điểm command; LS tạo child run với parent lineage và supersede run cũ sau khi child run được tạo thành công. Executable Review contract phải định nghĩa cách snapshot mới và optional reviewer context được truyền, không yêu cầu implementation suy diễn từ run cũ.
- Không có Reject action/state riêng.
- Review command dùng một Generic Review endpoint, idempotency key và
expected_run_version. - Stale review command trả
HTTP 409; UI reload state và không tự replay command cũ. - Free-text reviewer reason có thể tồn tại trong active retention window nhưng phải bị redacted theo mục Privacy, retention và audit; structured audit sau redaction không phụ thuộc free text.
Completion events và Transactional Outbox
- Run transition và outbox event được commit cùng transaction.
- Kill switch chặn initiation mới nhưng không được làm rơi completion webhook hoặc writeback retry của các run đã accepted; các công việc đã accepted phải được drain hoặc terminalize có audit.
- Dispatcher gửi events at-least-once tới registered subscription endpoints với bounded retry và DLQ/terminal visibility.
- Caller không được cung cấp arbitrary callback URL trong create request.
- Event envelope chứa event identity/type/version, tenant, subject, config version,
run_id,run_created_at,occurred_atvà structured event data; không chứa V-Key hoặc raw PII/evidence. - Provisioning và authentication của registered subscriptions phải được chốt trong Contract Sprint trước webhook implementation; dynamic registration UI/API không phải requirement của Pilot.
- Polling run status là fallback cho simple/internal clients.
Event ordering tại Omni
- Latest applicable run là run có
run_created_atmới nhất cho cùng(tenant_ref, subject_ref / ems_lead_id, scoring_config_version). - Event và Marker Block mang
run_idcùngrun_created_at. - Stale event hoặc same-run duplicate đã writeback là no-op, được ack
HTTP 200và ghi audit reason. event.occurred_atkhông dùng để xác định latest run.- Hai run khác nhau cùng
run_created_attạo ordering conflict; consumer giữ state hiện tại và không tự ghi đè. - Pilot không thêm Redis lock, distributed lock hoặc event-id idempotency table chỉ để xử lý ordering.
EMS Marker Block writeback
- Omni đọc EMS Lead
descriptionmới nhất trước khi merge/write. - Đúng một Marker Block hợp lệ được replace toàn khối, bảo toàn user text bên ngoài.
- Không có marker tag thì append block mới.
- Orphan tag hoặc nhiều block là malformed: không sửa description, đặt writeback terminal với
marker_block_malformedvà không auto-retry. - EMS/network transient errors là retryable và dùng bounded backoff. Exact classification cho authentication, permission, not-found, rate-limit, timeout và HTTP 5xx thuộc consumer contract/implementation spec; chúng không được hard-code từ giả định trong master spec.
- Writeback lifecycle thuộc Omni và không thay đổi completed scoring lifecycle tại LS.
- Xóa toàn bộ Marker Block không có nghĩa disable writeback; disable chỉ qua feature/configuration.
- Exact Marker template và merge examples thuộc shared fixtures, không copy bằng prose khác nhau giữa hai implementations.
Privacy, retention và audit
- Raw conversation, snapshot body/hash, direct PII, AI/reviewer free text và personal derived fields bị xóa/redact sau 90 ngày.
- Nếu Omni đã có reliable hard-deletion/privacy-erasure signal trong Pilot, LS nhận signal để redact sớm; nếu chưa có thì integration này được defer, scheduled retention vẫn bắt buộc.
- Sau redaction chỉ giữ structured audit tối thiểu: run/correlation IDs, tenant/subject refs, timestamps, status, scores/confidence/classification, config/model versions, scoring mode/degraded/error, token counts, review decision/numeric override, writeback outcome và non-personal structured codes.
- “Structured audit” là yêu cầu ở cấp hệ thống, không bắt mọi field nằm trong LS database. Writeback status/outcome thuộc Omni; Pilot không yêu cầu callback chỉ để sao chép writeback state về LS.
- Reviewer actor identity không được giữ sau post-redaction boundary trong Pilot.
- Structured audit tồn tại khi tenant active và bị purge khi tenant offboard.
- Pilot không hỗ trợ retention period tùy chỉnh theo tenant.
Dev Console boundary
- Dev Console là internal Svelte/TypeScript test surface, không phải customer-facing Pilot UI.
- Dev Console dùng cùng generated API client/contract và server-side test V-Key riêng.
- Raw test V-Key không xuống browser.
- Console có thể chạy fixtures, inspect run status, test effort/failure paths và một allowlisted integration tenant; nó không duplicate Omni Settings hoặc HITL business UI.
Shared Executable Contract Pack
- Contract Pack phải cung cấp generated OpenAPI, generated JSON Schemas, machine-readable error/outcome taxonomies, security profile, golden request/response/event examples và Marker Block fixtures.
- Contract Pack phải bao phủ Scoring Config Management contract đủ để Omni đọc, tạo và chọn/apply immutable config versions mà không sở hữu một config copy authoritative.
- Generic Review contract phải định nghĩa decision variants, success response, status/polling và event semantics.
- Subscriber contract phải định nghĩa authentication/rotation/replay, ingest acknowledgement cho accepted/duplicate/stale/invalid deliveries, và tách webhook ingest thành công khỏi EMS writeback hoàn tất.
- Contract Pack bắt đầu bằng pre-release version và được version/pin để Omni và LS chạy producer/consumer validation trên cùng artifacts.
- Admission HTTP errors, asynchronous run outcomes và Omni writeback errors là ba lifecycle khác nhau; taxonomy phải ghi rõ owner, transport và retry semantics.
- Generic Review API là một endpoint với decision variants, không tách thành ba endpoint không cần thiết.
- Contract distribution mechanism và webhook authentication là technical decisions cần chốt trong Contract Sprint.
- Security profile phải ghi rõ approved service-token mechanism: header, caller mapping, scope/tenant binding, rotation/revocation và authentication failure semantics; không được biến
X-Tenant-VKeyhoặc self-asserted app headers thành caller identity.
Delivery boundaries
- Contract Sprint phát hành executable contracts và fixtures trước khi hai repository triển khai sâu theo payload.
- Sau contract baseline, AI Platform và DotB-Omni triển khai song song qua provider/consumer specs riêng.
- AI Platform lane gồm service shell, contract models, run/config persistence, state machine, AIHub adapter, review API, outbox, retention và internal console boundary.
- Omni lane gồm feature gating, identity resolution, Snapshot Builder, client, Captain tool, webhook consumer, ordering/writeback state, Marker parser/worker và Settings/HITL UI.
- Hai lanes hội tụ ở bidirectional contract tests, sau đó E2E sandbox và failure injection.
- E2E runbook được hoàn thiện khi các executable components và integration environment tồn tại; không ghi secret thật.
Customer Pilot exit criteria
- Không có raw V-Key/PII leakage trên các persistent và observability surfaces đã xác định.
- Manual và Captain E2E flows hoạt động với fallback, HITL, completion event và EMS writeback.
- Không có silent stuck runs, outbox deliveries hoặc writeback jobs.
- Retention cleanup và kill switch được kiểm chứng bằng executable tests/drills.
- Không còn P0/P1 mở thuộc Pilot scope.
- Allowlisted tenant hoàn thành duration/run volume đã thỏa thuận.
- Pilot thu baseline latency, AIHub failure, HITL, override, token usage, outbox và writeback metrics.
- Không dùng threshold SLO/quality suy đoán làm exit gate trước baseline.
Testing Decisions
- Tests ưu tiên external behavior và contract seams; không khóa tests vào class layout, private methods hoặc implementation topology có thể thay đổi.
- Shared fixtures phải là cùng một corpus cho provider validation và Omni consumer contract tests; không duy trì payload giả cạnh tranh trong từng repository.
- Contract generation phải deterministic và verification phải fail khi generated OpenAPI/schema drift khỏi committed artifacts.
- Domain tests phải bao phủ deterministic identity ranking, snapshot caps/truncation, scoring formula bounds, state transitions, idempotency, review concurrency, latest-run ordering và Marker Block merge outcomes.
- API tests phải bao phủ admission success/validation,
202, polling status, idempotency replay, payload mismatch409, review decisions và review-version conflict. - Worker/fault-injection tests phải bao phủ worker interruption, AIHub timeout/error/quota, partial AI pass failure, outbox retry/DLQ và stuck-run recovery.
- Security tests phải dùng sentinel credentials để chứng minh V-Key không xuất hiện trong database, queue/checkpoint, logs, traces, error payloads, generated events hoặc browser-visible responses.
- Privacy tests phải chứng minh 90-day redaction xóa evidence/PII/free text/personal derived data nhưng giữ đúng structured audit, đồng thời purge tenant data khi offboard.
- Omni consumer tests phải bao phủ feature gating, RBAC enforcement, Identity Resolution v1, Snapshot schema validation, V-Key-safe client behavior, event auth/validation, stale/duplicate/order conflict và writeback retry classification.
- Marker Block tests phải dùng shared text fixtures cho append, replace, duplicate/stale no-op, orphan start/end, multiple blocks và user text preservation.
- Captain tests phải chứng minh explicit tool invocation, duplicate protection, EMS-first partial failure behavior và không đưa raw V-Key vào LLM-visible context.
- Dev Console tests phải chứng minh generated client compatibility và không expose server-side test V-Key.
- E2E integration tests phải chạy tối thiểu manual happy path, Captain partial failure, degraded AI fallback, review conflict, completion delivery, stale event, malformed Marker Block, EMS timeout và kill-switch behavior.
- Retention 90 ngày không cần test bằng chờ thời gian thật; clock được kiểm soát tại boundary để cleanup có deterministic verification.
oasdiffchỉ trở thành blocking compatibility check sau khi có released OpenAPI baseline; schema/property testing được bật khi real app/auth/external overrides deterministic.- Pilot readiness cần cả automated verification và operational drill cho kill switch, outbox backlog, worker interruption, AIHub outage, EMS outage và credential leak suspicion.
Out of Scope
- Opportunity Scoring hoặc Contact Scoring độc lập.
- Customer-facing configuration/HITL frontend trong repository AI Platform.
- HITL UI nhúng trực tiếp trong DotB EMS.
- EMS custom database fields dành riêng cho Lead Scoring trong Pilot.
- Tự động map score/classification sang Contact Stage hoặc EMS Lead status.
- Generic automatic input-event trigger như message-created, conversation-created/resolved hoặc EMS lifecycle event.
- Public Internet API hoặc arbitrary caller callback URL.
- Batch Scoring API; caller-side fan-out là strategy hiện tại nếu có use case được phê duyệt.
- Dedicated broker như Kafka hoặc RabbitMQ.
- Exactly-once AIHub invocation, AIHub response cache hoặc provider billing deduplication.
- Automatic LS retry cho ambiguous AIHub failures.
- Autonomous AI tools, Internet search hoặc unbounded agent loops trong scoring pipeline.
- Dynamic per-tenant retention policy trong Pilot.
- General-purpose subject-erasure/privacy orchestration platform; early deletion chỉ tích hợp nếu Omni đã có reliable signal.
- Dynamic webhook registration UI/API nếu static/managed registered subscriptions đáp ứng Pilot; exact provisioning mechanism thuộc Contract Sprint.
- Score/confidence authoritative persistence trong Omni hoặc scoring config authoritative copy ngoài LS.
- Score-to-stage automation.
- Reuse production Omni tenant V-Key trong Dev Console ngoài integration test được phê duyệt.
- Reuse nguyên trạng runtime architecture, CRM adapter, in-memory checkpoint hoặc legacy HITL semantics của
lead-scoring-agent. - Production deployment platform, autoscaling topology, monitoring vendor hoặc infrastructure provisioning trong spec này; chúng phải thỏa invariants trước Pilot deployment.
Further Notes
Source hierarchy
- Tài liệu này là master build specification sau khi được approved.
- Exact wire contract được phát hành qua Shared Executable Contract Pack do AI Platform sở hữu và DotB-Omni review.
- RFC, Current Status và Grill Brief trong DotB Vault là decision history/context và phải được cập nhật hoặc đánh dấu superseded khi mâu thuẫn với spec approved.
stack.mdtiếp tục ghi technology decisions/candidates. Mọi trạng thái “selected” chỉ có nghĩa adopted khi đã được spec hoặc explicit decision phê duyệt.
Known contract clarifications carried into Contract Sprint
- LS sinh
run_id; create request không gửirun_id. - Same idempotency key với normalized payload khác trả
HTTP 409. - Generic Review dùng một endpoint và decision variants.
- Automatic input-event trigger bị defer; completion events vẫn thuộc Pilot.
- Webhook subscriber authentication, secret rotation, replay handling và acknowledgement semantics phải được chốt trước webhook implementation.
- Contract packaging/version pinning phải cho phép cùng fixtures chạy ở producer và consumer repositories.
Service implementation specs
- AI Platform cần một provider implementation spec sau contract baseline, tập trung vào service/runtime/persistence/worker seams và không mô tả Omni UI/Rails internals.
- DotB-Omni cần một consumer implementation spec sau khi Omni grilling hoàn tất, tập trung vào identity/snapshot/client/RBAC/webhook/writeback/UI/Captain seams và không copy exact API/schema.
- Hai spec con phải tham chiếu master spec và một Contract Pack version cụ thể.
Delivery graph
Master spec approved
↓
Contract Pack baseline
↓
┌────────────────────────┬──────────────────────────┐
│ AI Platform provider │ DotB-Omni consumer │
│ implementation │ implementation │
└────────────────────────┴──────────────────────────┘
↓
Bidirectional contract verification
↓
E2E sandbox + fault injection
↓
Allowlisted Customer Pilot + baseline collection
Remaining technical decisions
Các quyết định sau không làm product frontier mở lại, nhưng phải được ghi và kiểm chứng trước subsystem tương ứng hoặc Pilot deployment:
- Production database và migration tooling.
- Worker/runtime scheduling topology và stuck-run lease/timeout implementation.
- Registered subscriber provisioning và authentication.
- Contract artifact distribution/version pinning.
- Per-environment provisioning, rotation/revocation và operational ownership cho service tokens đã được approve.
- Hosting, network exposure và deployment ownership trước Customer Pilot deployment.
- Exact model mapping, token budget và timeout cho từng AI effort profile.
- Metrics/alerting backend và operational ownership.
Readiness for ticketing
Các điều kiện chuyển sang /to-tickets đã được đáp ứng ngày 2026-08-26:
- AI Platform, DotB-Omni và AIHub đã review các boundary liên quan.
- Tài liệu discovery mâu thuẫn đã được cập nhật hoặc đánh dấu superseded/historical.
- Spec có trạng thái
approvedvà version1.0.0. - Contract Sprint được xác định là blocker đầu tiên cho các consumer/provider tickets phụ thuộc payload.
Spec sẵn sàng làm đầu vào cho /to-tickets; việc tạo ticket chưa được thực hiện trong migration tài liệu này.