Sheet 3 · Chi tiết đối tượng dữ liệu

Chi tiết đối tượng dữ liệu

Nguồn sự thật: frontend-next/src/erp/types/index.ts (27 interface — dữ liệu nghiệp vụ nằm trong blob sync) và sync-server/tenant.js (8 bảng server riêng, ngoài blob). Tên field/kiểu chép nguyên văn từ code, KHÔNG diễn giải thêm. Cột "Bắt buộc" = KHÔNG có dấu ? sau tên field trong TypeScript.

Nhóm A — Trong blob sync (đồng bộ realtime, đọc/ghi qua pullState() / PUT /api/state)

Task 14 field module tasks

Công việc — có bảng vật lý riêng tasks (materialized view từ blob) để lọc/sort phía server.

FieldKiểuBắt buộcMô tả
idstringxKhoá chính
keystringxMã hiển thị dạng CTC-16
titlestringxTên công việc
descriptionstringxMô tả (hỗ trợ @mention qua MentionTextarea)
statusTaskStatus = todo|in_progress|review|donexTrạng thái kanban
priorityPriority = urgent|high|medium|lowxƯu tiên
assigneeIdstringxUser phụ trách (khoá vào auth_users.id)
projectIdstringDự án liên quan
contactIdstringĐối tác liên quan
dueDatestringxHạn hoàn thành
tagsstring[]xTag tự do + có cấu trúc (viec:*, gp:*)
subtasksSubTask[]xViệc con — từng crash trắng trang khi field này undefined (đã vá 2 lớp 28/07)
sortOrdernumberxThứ tự kéo-thả kanban
createdAtstringxNgày tạo
completedAtstringNgày hoàn thành — set/clear tự động khi đổi status (từ GĐ Analytics)
archivedbooleanĐã lưu trữ (backlog)
linksDocLink[]Tài liệu đính kèm
SubTask 3 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
titlestringxTên việc con
donebooleanxĐã hoàn thành
DocLink 3 field

Dùng chung cho Task/Project/Invoice/Transaction/Vendor... (tài liệu đính kèm)

FieldKiểuBắt buộcMô tả
idstringxKhoá
titlestringxTên hiển thị
urlstringxĐường dẫn
Contact 16 field module crm

Đối tác/khách hàng — bảng contacts.

FieldKiểuBắt buộcMô tả
idstringxKhoá chính
namestringxTên liên hệ
companystringxTên công ty
phonestringxĐiện thoại
emailstringxEmail
taxCodestringxMã số thuế
addressstringxĐịa chỉ
provincestringxTỉnh/thành
statusContactStatus = lead|prospect|customer|inactivexTrạng thái quan hệ
dealStageDealStagexCòn trong schema Contact nhưng deal đã chuyển sang sống trên Project — field lịch sử
dealValuenumberxKHÔNG còn dùng để hiển thị (từ 20/08) — UI đọc rollup tính từ Project thay thế, xem trang Tổng quan mục "Rollup giá trị đối tác"
sourcestringxNguồn khách hàng
tagsstring[]xTag, gồm nganh:* + gp:*
notesstringxGhi chú tự do
assigneeIdstringxNgười phụ trách — dùng để scope quyền member
createdAtstringxNgày tạo
lastContactDatestringxLần chăm sóc gần nhất
stageHistoryStageHistoryEntry[]xLịch sử đổi giai đoạn
closedReasonstringLý do đóng (won/lost)
StageHistoryEntry 2 field
FieldKiểuBắt buộcMô tả
stageDealStagexGiai đoạn
enteredAtstringxThời điểm vào giai đoạn
Activity 8 field module crm

Tương tác với đối tác (gọi/zalo/email/gặp/ghi chú) — bảng activities.

FieldKiểuBắt buộcMô tả
idstringxKhoá
contactIdstringxĐối tác liên quan
typeActivityType = call|zalo|email|meeting|notexLoại tương tác
datestringxNgày
summarystringxTóm tắt
nextStepstringBước tiếp theo
nextStepDatestringNgày hẹn tiếp theo
userIdstringxNgười ghi nhận
createdAtstringxNgày tạo
Project 17 field module projects

Dự án — bảng projects. Từ 27/07 mang luôn "deal" (bán hàng), tách khỏi status (giao hàng).

FieldKiểuBắt buộcMô tả
idstringxKhoá chính
namestringxTên dự án
descriptionstringxMô tả
statusProjectStatus = planning|active|on_hold|acceptance|completedxTrạng thái giao hàng/triển khai
progressnumberx% tiến độ (0-100)
budgetnumberxNgân sách sản xuất
spentnumberxĐã chi
startDatestringxNgày bắt đầu
endDatestringxNgày kết thúc
memberIdsstring[]xThành viên dự án — dùng scope quyền member
colorstringxMàu hiển thị
priorityPriorityxƯu tiên
createdAtstringxNgày tạo
archivedbooleanĐã lưu trữ
linksDocLink[]Tài liệu
tagsstring[]Tag gp:* (giải pháp — không có bộ tag riêng cấp dự án)
dealStageDealStageGiai đoạn bán — nhãn hiển thị khác nhau khi xem từ Đối tác (nhãn "quan hệ") vs xem từ Dự án (nhãn "bán hàng"), cùng 1 bộ giá trị
dealValuenumberGiá trị hợp đồng — hiện trên thẻ phễu; nguồn của rollup ở Contact
contactIdstringĐối tác sở hữu (1 dự án — 1 đối tác, quy tắc nghiệp vụ không cho N-N)
CalendarEvent 10 field module calendar
FieldKiểuBắt buộcMô tả
idstringxKhoá
titlestringxTiêu đề
typemeeting|task|reminder|deadlinexLoại sự kiện
datestringxNgày
timestringGiờ
durationnumberThời lượng (phút)
descriptionstringMô tả
assigneeIdsstring[]xNgười tham gia — rỗng = sự kiện chung, ai cũng thấy (quy ước scope quyền)
colorstringxMàu hiển thị
linkstringLiên kết đính kèm
recurringIdstringSinh ra từ EventRecurrence nào
EventRecurrence 12 field

Sự kiện lặp — engine sinh CalendarEvent tự động theo freq.

FieldKiểuBắt buộcMô tả
idstringxKhoá
titlestringxTiêu đề mẫu
typeCalendarEvent['type']xLoại
timestringGiờ
durationnumberThời lượng
descriptionstringMô tả
assigneeIdsstring[]xNgười tham gia
colorstringxMàu
linkstringLiên kết
freqweekly|monthlyxTần suất lặp
startDatestring (YYYY-MM-DD)xNgày bắt đầu lặp
activebooleanxĐang bật
lastGenstring (YYYY-MM-DD)xĐã sinh sự kiện tới ngày nào ('' = chưa sinh lần nào)
createdAtstringxNgày tạo
Invoice 14 field module invoices
FieldKiểuBắt buộcMô tả
idstringxKhoá
numberstringxSố hoá đơn (vd CTC202600001)
typegtgt|ban_hangxLoại hoá đơn
statusInvoiceStatus = draft|sent|paid|overdue|cancelledxTrạng thái
contactIdstringxKhách hàng
issueDatestringxNgày phát hành
dueDatestringxHạn thanh toán — dùng tính daysOverdue
itemsInvoiceItem[]xDòng hàng hoá/dịch vụ
subtotalnumberxTổng trước thuế
taxRatenumberxThuế suất
taxAmountnumberxTiền thuế
totalnumberxTổng thanh toán
notesstringxGhi chú
bankAccountstringxSTK nhận tiền
bankNamestringxNgân hàng
createdAtstringxNgày tạo
linksDocLink[]Đính kèm (ảnh OCR quét...)
Type Invoice CHƯA khai tags?: string[] — trang Quản lý tag vẫn đọc/ghi field này qua ép kiểu an toàn (ghi trong CONTEXT.md gốc, khuyến nghị bổ sung vào type khi tiện).
InvoiceItem 6 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
descriptionstringxMô tả hàng hoá
unitstringxĐơn vị tính
quantitynumberxSố lượng
unitPricenumberxĐơn giá
amountnumberxThành tiền
Transaction 10 field module finance
FieldKiểuBắt buộcMô tả
idstringxKhoá
typeTransactionType = income|expensexLoại giao dịch
categorystringxDanh mục
amountnumberxSố tiền
descriptionstringxDiễn giải
datestringxNgày
referencestringxSố tham chiếu
contactIdstringĐối tác liên quan
invoiceIdstringHoá đơn liên quan
projectIdstringDự án liên quan
tagsstring[]xTag tc:*
linksDocLink[]Đính kèm
recurringIdstringSinh ra từ Recurrence nào — kế thừa tags/contactId/projectId của recurrence gốc
Recurrence 10 field

Giao dịch định kỳ — engine tự sinh Transaction hàng tháng.

FieldKiểuBắt buộcMô tả
idstringxKhoá
typeTransactionTypexLoại
categorystringxDanh mục
amountnumberxSố tiền
descriptionstringxDiễn giải
dayOfMonthnumber (1-31)xNgày lặp trong tháng, tự kẹp theo số ngày thực của tháng
referencestringxSố tham chiếu
linksDocLink[]Đính kèm
activebooleanxĐang bật
lastRunstring (YYYY-MM)xĐã sinh giao dịch tới tháng nào
createdAtstringxNgày tạo
Notification 6 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
typetask|invoice|reminder|systemxLoại
titlestringxTiêu đề
messagestringxNội dung
readbooleanxĐã đọc
createdAtstringxNgày tạo
linkstringĐường dẫn liên quan
AppSettings 6 field module settings
FieldKiểuBắt buộcMô tả
companyNamestringxTên công ty
taxCodestringxMã số thuế
addressstringxĐịa chỉ
bankAccountstringxSTK
bankNamestringxNgân hàng
defaultTaxRatenumberxThuế suất mặc định
invoicePrefixstringxTiền tố số hoá đơn
Vendor 11 field module budget-comms

Nhà cung cấp (NCC) gắn 1 chiến dịch — module Ngân sách truyền thông.

FieldKiểuBắt buộcMô tả
idstringxKhoá
projectIdstringxChiến dịch (project)
namestringxTên NCC
categorystringxLoại hình
taxCodestringMST
contractNostringSố hợp đồng
contractLinkstringLink hợp đồng
contractValuenumberxGiá trị hợp đồng (chưa VAT)
vatRatenumberxThuế suất VAT
acceptedValuenumberxNghiệm thu thực tế — có thể vượt contractValue (overage)
acceptedClientApprovedbooleanxCĐT đã duyệt phần vượt?
statusVendorStatus = signed|in_progress|accepted|paidxTrạng thái
createdAtstringxNgày tạo
VendorPayment 8 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
vendorIdstringxNCC
amountnumberxSố tiền
paidDatestringNgày chi
proofstringChứng từ
approvedbooleanxĐã duyệt
approvedBystringNgười duyệt
createdBystringNgười tạo
selfApprovedbooleanTự duyệt (không qua người khác)
createdAtstringxNgày tạo
CampaignBudget 5 field

Cấu hình "Lớp A" cho từng chiến dịch — 2 công thức tính ngân sách.

FieldKiểuBắt buộcMô tả
projectIdstringxChiến dịch
methodBudgetMethod = percent|reservexCông thức
percentnumberDùng khi method=percent
reserveFixednumberDùng khi method=reserve
reserveMarginnumberBiên dự phòng
KeyMessage 9 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
projectIdstringxChiến dịch
phasestringxGiai đoạn (vd "GĐ1-Booming")
monthstring (YYYY-MM)Tháng
productstringSản phẩm
messagestringxThông điệp chính
tonestringGiọng điệu
sourceChunkstringGrounding chống bịa — bắt buộc khi AI trích xuất trực tiếp
approvednumber (1|0|-1)x1 đã duyệt · 0 chờ · -1 loại
createdAtstringxNgày tạo
BcChange 9 field

Nhật ký thay đổi kiểu NotebookLM — trích dẫn + minh chứng before→after.

FieldKiểuBắt buộcMô tả
idstringxKhoá
projectIdstringxChiến dịch
tsstringxThời điểm
actorstringxNgười/thứ thực hiện
actionstringxpayment|overage_approved|key_extracted|key_approved|conflict_resolved|article_decided|content_accepted|vendor_updated|sheet_sync
entitystringxĐối tượng bị đổi (vd "NCC: IFTV")
summarystringxTóm tắt
beforestringGiá trị trước
afterstringGiá trị sau
citationstringNguồn dẫn chứng
KeyConflict 7 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
projectIdstringxChiến dịch
newKeyIdstringxKey message mới
existingKeyIdstringxKey message đang có
phasestringGiai đoạn
productstringSản phẩm
statusopen|resolvedxTrạng thái xử lý
resolutionkeep_existing|use_new|mergeCách giải quyết
createdAtstringxNgày tạo
ContentItem + ContentBrief 10+4 field
FieldKiểuBắt buộcMô tả
idstringxKhoá
projectIdstringxChiến dịch
typestringxpost|emag|seeding|video
titlestringxTiêu đề
channelstringKênh đăng
phasestringGiai đoạn
productsstring[]xSản phẩm liên quan
primaryProductstringSản phẩm chính
statusContentStatus (7 trạng thái: plan→brief_review→brief_revise→brief_approved→article_review→approved→published, cộng accepted)xTrạng thái duyệt bài
draftstringBản nháp
briefsContentBrief[]xLịch sử brief — mỗi bản có version, body, status(review|approved|rejected), createdAt
publishedLinkstringLink đã đăng
vendorIdstringNCC liên quan (nghiệm thu → trigger thanh toán)
deadlinestringHạn
createdAtstringxNgày tạo
Comment + CommentPin + CommentMessage 5+6+3 field Review layer

Cơ chế review kiểu Figma — pin nhận xét neo theo trường dữ liệu hoặc toạ độ % trên trang.

FieldKiểuBắt buộcMô tả
idstringxKhoá Comment
pin.cidstring"<entity_type>:<entity_id>:<field_path>" — ưu tiên neo theo entity
pin.pageKeystringPathname trang — dùng khi neo toạ độ tự do
pin.xPct / pin.yPctnumberToạ độ % trong <main>
pin.labelstringNhãn gọn hiển thị
pin.snapshotstringGiá trị field lúc pin — chống lệch khi số liệu đổi sau đó
thread[].id/author/body/tsCommentMessage[]x[0]=comment gốc, còn lại là reply; author = userId
resolved / resolvedBy / resolvedAtboolean/string/stringresolved bắt buộcTrạng thái xử lý
createdAtstringxNgày tạo
Đây là Comment trong blob (đồng bộ qua store.comments, thuộc CLIENT_SCHEMA v14) — KHÁC với bảng SQL comments ở Nhóm B bên dưới (tiền thân cũ, còn tồn tại trong tenant.js nhưng không có route server.js nào ghi vào — suy luận đây là schema dự phòng/legacy chưa dọn, không phải nguồn dữ liệu review hiện dùng).

Nhóm B — Bảng server riêng (ngoài blob, per-owner, KHÔNG qua /api/state)

20 bảng có tên cố định, khai tường minh trong tenant.js: revisions · settings · worklog · comments · share_tokens · auth_users · user_data · workflows · workflow_runs · workflow_state · skills · dev_requests · library_items · library_boards · bots · bot_chat_links · bot_link_codes · bot_api_keys · quotes · contracts. Cộng 9 bảng thực thể sinh trong vòng lặp là 29 bảng mỗi tenant — đếm thật trong sqlite_master.

Nguồn: CREATE TABLE trong sync-server/tenant.js. Không phải TypeScript interface (không có type FE tương ứng — FE tự định nghĩa type cục bộ khi gọi API), liệt kê theo đúng tên cột SQL.

auth_users 9 cột
CộtKiểu SQLMô tả
idTEXT PKKhoá — quy ước u1..u7 cho nhân sự thật (không phải random)
usernameTEXT UNIQUE NOT NULLĐăng nhập
pass_hashTEXT NOT NULLMật khẩu đã băm
display_nameTEXT DEFAULT ''Tên hiển thị
roleTEXT DEFAULT 'member'admin|member
modulesTEXT DEFAULT '[]'Mảng JSON module được cấp
activeINTEGER DEFAULT 1Còn hoạt động
created_atTEXT NOT NULLNgày tạo
titleTEXT DEFAULT ''Chức danh tự do — cột thêm sau qua ALTER TABLE, tách khỏi role
data_scopeTEXT NOT NULL DEFAULT ''Trục phạm vi dữ liệu (thêm 02/09): self · team · all. Độc lập với role — quản trị vẫn có thể bị hạn chế phạm vi. Rỗng = suy theo role.
session_verINTEGER NOT NULL DEFAULT 0Số phiên bản phiên (thêm 02/09): chữ ký cookie phủ cả cột này, nên tăng nó là mọi phiên cũ của người đó mất hiệu lực ngay. Dùng cho POST /api/me/sessions/revoke-all.

Quy ước id: u0..u7 cho nhân sự thật thời đầu, tài khoản thêm sau dùng id sinh ngẫu nhiên dạng au_<hex> — cả hai dạng cùng tồn tại trong tenant ctc.

bot_api_keys 11 cột khoá cho trợ lý ngoài

Bảng then chốt của kiến trúc "trợ lý gọi vào ERP". Mỗi khoá là một danh tính có phạm vi cố định.

CộtKiểu SQLMô tả
idTEXT PKKhoá
bot_idTEXTBot Telegram gắn kèm, nếu có
created_byTEXTNgười cấp khoá
scope_user_idTEXT NOT NULLGắn chết một tài khoản. ERP tra khoá ra người này rồi áp nguyên quyền của họ — bên ngoài không tự khai danh tính được.
labelTEXT DEFAULT ''Ghi chú do người cấp đặt (vd workspace:linh)
key_hashTEXT NOT NULLChỉ lưu SHA-256. Bản rõ hiện đúng một lần lúc cấp, không lấy lại được.
key_prefixTEXTVài ký tự đầu để nhận diện trên giao diện (vd erpbot_a498f…)
skill_idsTEXT NOT NULL DEFAULT '[]'Kỹ năng phát hành cho khoá này — agent cầm khoá mới biết các kỹ năng đó
last_used_atTEXTLần gọi cuối. Cột chẩn đoán quan trọng nhất: bot báo "đã làm" mà cột này không đổi thì lời gọi chưa hề tới ERP.
revoked_atTEXTThu hồi — có hiệu lực tức thì, khoá đã thu hồi không hiện trên giao diện
created_atTEXTNgày cấp
workflows 8 cột module workflows
CộtKiểu SQLMô tả
idTEXT PKKhoá
owner_idTEXT NOT NULLChủ sở hữu — GET chỉ trả của mình + được share, kể cả admin
nameTEXT DEFAULT ''Tên quy trình
activeINTEGER DEFAULT 0Đang bật. Vá 03/09: upsertWorkflow chỉ đổi cột này khi người gọi thực sự gửi trường active — trước đó mọi lời gọi thiếu trường đều bị hiểu là lệnh tắt, làm quy trình đang chạy dừng lặng lẽ.
defTEXT DEFAULT '{}'JSON định nghĩa — dạng graph (nodes, edges) (builder canvas @xyflow/react)
shared_withTEXT DEFAULT '[]'Mảng userId được chia sẻ (viewer)
versionsTEXT DEFAULT '[]'Lịch sử phiên bản, cap 10 — server tự đẩy version khi def đổi
created_at / updated_atTEXTMốc thời gian
workflow_runs 6 cột
CộtKiểu SQLMô tả
idTEXT PKKhoá
wf_idTEXT NOT NULLWorkflow chạy
user_idTEXT NOT NULLNgười/máy chạy
tsTEXT NOT NULLThời điểm chạy
resultTEXT NOT NULLKết quả (thành công/lỗi)
noteTEXT DEFAULT ''Ghi chú — cap 500 dòng/workflow
skills 9 cột module skills
CộtKiểu SQLMô tả
idTEXT PKKhoá
owner_idTEXT NOT NULLChủ sở hữu
nameTEXT DEFAULT ''Tên kỹ năng
categoryTEXT DEFAULT ''Danh mục
defTEXT DEFAULT '{}'JSON (summary, steps, checklist, tags, resources)
shared_withTEXT DEFAULT '[]'Chia sẻ mức Xem — đọc và nhân bản, không sửa được
shared_editTEXT NOT NULL DEFAULT '[]'Cột mới 03/09 — chia sẻ mức Sửa: sửa được nội dung nhưng KHÔNG đổi được chia sẻ, KHÔNG phát hành, KHÔNG xoá. Tách riêng khỏi shared_with để không phải đoán ý bản ghi cũ: mọi chia sẻ trước 03/09 đều là chỉ-xem.
versionsTEXT DEFAULT '[]'Lịch sử phiên bản, cap 20
publishedINTEGER DEFAULT 0Đã phát hành (viewer chỉ xem + Nhân bản)
created_at / updated_atTEXTMốc thời gian
dev_requests 10 cột module requests
CộtKiểu SQLMô tả
idTEXT PKKhoá
requester_idTEXT NOT NULLNgười gửi yêu cầu
kindTEXT NOT NULLworkflow|skill
titleTEXT DEFAULT ''Tiêu đề
bodyTEXT DEFAULT ''Mô tả (hỗ trợ @mention)
target_idTEXTWorkflow/Skill liên quan (nếu có)
statusTEXT DEFAULT 'new'new|reviewing|approved|rejected|released
reviewer_idTEXTAdmin duyệt
review_noteTEXT DEFAULT ''Ghi chú duyệt
released_versionTEXT DEFAULT ''Phiên bản đã phát hành
created_at / updated_atTEXTMốc thời gian
user_data 4 cột

Key-value per-user, whitelist key: workflows, workflow-runs, prefs, notify-prefs, integrations, income-config, quotes, mail-status… (≤256KB/giá trị).

CộtKiểu SQLMô tả
user_idTEXT (PK cùng k)Chủ sở hữu
kTEXT (PK cùng user_id)Tên key
vTEXT DEFAULT ''Giá trị JSON
updated_atTEXTMốc thời gian
worklog 7 cột
CộtKiểu SQLMô tả
idTEXT PKKhoá
tsTEXT NOT NULLThời điểm
sourceTEXT NOT NULLNguồn ghi log
author / chatTEXTNgười/kênh gửi
project_idTEXTDự án liên quan
text / rawTEXTNội dung log / dữ liệu thô
share_tokens 7 cột
CộtKiểu SQLMô tả
idTEXT PKKhoá
project_idTEXT UNIQUE NOT NULLDự án được share
token_hashTEXT UNIQUE NOT NULLToken đã băm
client_nameTEXTTên khách xem
created_by / created_at / expires_atTEXTMốc & người tạo
revokedINTEGER DEFAULT 0Đã thu hồi
comments (SQL, legacy) 12 cột
CộtKiểu SQLMô tả
idTEXT PKKhoá
project_idTEXT NOT NULLDự án
entity_type / entity_id / field_pathTEXTNeo bản ghi
parent_idTEXTReply cha
author_type / author_nameTEXT NOT NULLNgười bình luận
bodyTEXT NOT NULLNội dung
resolved / resolved_by / resolved_atmixedXử lý
created_at / anchorTEXTMốc & vị trí neo
revisions 5 cột NGUỒN SỰ THẬT

Bảng quan trọng nhất trong toàn hệ thống, và trước bản này tài liệu chưa mô tả nó. Toàn bộ dữ liệu nghiệp vụ của một tenant nằm ở đây dưới dạng một chuỗi JSON duy nhất; mọi bảng thực thể (tasks, contacts, projects…) chỉ là bản dựng lại từ nó.

CộtKiểu SQLMô tả
revINTEGER PK AUTOINCREMENTSố phiên bản tăng dần. Client giữ rev đang có; server so để biết cần đẩy gì qua SSE.
stateTEXTToàn bộ state client dạng JSON, cấu trúc lồng {version: 14, state: {…}}. CLIENT_SCHEMA = 14 và KHÔNG được bump — bump là mọi client cũ mất dữ liệu.
updated_atTEXTThời điểm ghi
actor_idTEXT NOT NULL DEFAULT ''Thêm 02/09. Ai ghi bản này. Trước đó không truy được thay đổi về người nào.
sourceTEXT NOT NULL DEFAULT 'system'Thêm 02/09. Ghi qua đường nào: web · api-key · agent · system — phân biệt được thay đổi đến từ trợ lý hay từ người thật.

Ghi vào bảng này KHÔNG bao giờ là ghi đè thẳng: mergeScopedState nhận state client gửi lên, giữ nguyên phần ngoài phạm vi của người đó, chạy sanitizeRefs ép các tham chiếu ngoài phạm vi về rỗng, rồi mới tạo rev mới.

Trọng số bảng thực thể (materialized view — ENTITY_TABLES)

9 bảng sinh lại từ blob để phục vụ truy vấn nhanh (KHÔNG phải nguồn sự thật): users, tasks, contacts, activities, projects, events, invoices, transactions, notifications — mỗi bảng cấu trúc id TEXT PRIMARY KEY, data TEXT NOT NULL + vài cột index phụ (vd tasks có thêm status, assignee, due). Bảng users là "vestigial" — blob không còn top-level key users (đã thay bằng auth_users + /api/team).