State Machines
Mọi giá trị enum trong hệ thống, ai được đổi, và đổi theo đường nào. Trạng thái nằm rải rác trong code là nguồn của lỗi âm thầm — trang này gom về một chỗ.
Luật của trang này (AS-04.3.5): mỗi bảng D1 có cột status phải có một mục ở đây, và tập giá trị phải khớp đúng CHECK constraint trong migrations/. Lệch một giá trị là lệch tài liệu, không phải "gần đúng".
Hai loại trạng thái, đừng lẫn:
- Trạng thái lưu — có cột
statustrong D1, đổi bằng một hành động cụ thể của ai đó. §5 trở đi. - Trạng thái suy ra — không lưu, tính lại mỗi lần đọc từ số liệu model. §1–§4.
Phần A — Trạng thái suy ra (không lưu)
1. Skill state — mức vững một node
Suy ra từ (mastery, confidence), không lưu, tính bằng stateFor().
confidence < 0.25 → unknown ⚪ "Đang làm quen"
mastery ≥ 0.8 → chac 🟢 "Chắc rồi"
mastery ≥ 0.5 → lung_lay 🟡 "Đang lung lay"
còn lại → hong 🔴 "Cần xây lại từ gốc"Confidence luôn thắng: chưa đủ bằng chứng thì không được kết luận "hổng". Không có chuyển tiếp trực tiếp — trạng thái đổi khi mastery/confidence đổi.
Màu: đỏ không dùng cho trạng thái học tập; hong là coral ấm ("cần xây lại"), không phải màu lỗi (REQ-UX-03).
2. Need signal — "bài này có cần học không"
Nhãn hiển thị trong Phòng Lab, suy từ skill state:
| Need | Từ state | Nhãn |
|---|---|---|
urgent | hong | Cần học |
review | lung_lay | Nên ôn |
solid | chac | Đã vững |
new | unknown | Chưa học |
3. Retention group label
Từ current_retention (đã decay) + historical_mastery:
historical_mastery < 0.6 → null (chưa từng vững — không thuộc bức tranh trí nhớ)
retention ≥ 0.80 → dang_chac Đang chắc
retention ≥ 0.65 → nhac_lai Cần nhắc lại sớm
retention ≥ 0.45 → nguy_co_quen Có nguy cơ quên
còn lại → nen_on_lai Nên ôn lại4. Review urgency
NONE → LOW → MEDIUM → HIGH → CRITICAL. Bắt đầu từ thang retention rồi điều chỉnh theo ngữ cảnh:
| Điều kiện | Dịch chuyển |
|---|---|
historical_mastery < 0.6 | ép về NONE |
| thuộc goal và thi ≤14 ngày | +1 bậc |
| đang chặn ≥3 bài sau | +1 bậc |
| không thuộc goal nào và không có kỳ thi | −1 bậc (sàn LOW) |
Chi tiết: retention-model §6.1.
Phần B — Danh tính & phiên
5. User
users.status — migration 0001.
active ──┬──▶ suspended (vận hành khoá tạm, đăng nhập lại được sau khi mở)
└──▶ deleted (xoá mềm — hàng còn, dữ liệu trỏ tới đã đi qua đường xoá)deleted không phải xoá cứng: nhật ký truy cập và bản ghi đồng thuận vẫn phải trỏ về một hàng có thật. Xoá thật đi qua data_deletion_requests (§13). Ai đổi: chỉ vận hành/admin.
6. Session
sessions — migration 0001. Bảng không có cột status; trạng thái suy từ ba cột thời gian, và đó là chủ đích: một phiên chỉ có thể chết theo ba cách khác nhau về nguyên nhân.
(đăng nhập) ──▶ active ──┬──▶ rotated (quá 7 ngày → cấp token mới, rotated_from trỏ về bản cũ)
├──▶ expired (quá expires_at)
└──▶ revoked (revoked_at — đăng xuất/thu hồi)Session hợp lệ = revoked_at IS NULL và expires_at > now. Token lưu dưới dạng SHA-256 hash, không bao giờ lưu thô. client ∈ web | mobile quyết định luật CSRF nào áp dụng, không phải trạng thái.
7. Invitation
invitations.status — migration 0001.
pending ──┬──▶ accepted (accepted_by + accepted_at được điền)
├──▶ expired (quá expires_at — không ai bấm)
└──▶ revoked (người mời rút lại)Ba nhánh kết là cuối — không quay lại pending. Mời lại là tạo lời mời mới với code mới, không hồi sinh dòng cũ: mã cũ đã có thể lọt ra ngoài. role ∈ learner | guardian | supporter là vai sẽ được cấp khi chấp nhận, không phải trạng thái.
8. Learner
learners.status — migration 0001.
invited ──▶ active ──▶ paused ──▶ active
└──▶ archivedinvited— bố mẹ đã tạo hồ sơ, con chưa có tài khoản (user_id IS NULL). Đây là trạng thái bình thường, không phải dữ liệu dở dang.active— đang học.paused— tạm dừng (nghỉ hè, ốm dài). Dữ liệu giữ nguyên, engine không đẩy nhắc nhở.archived— không còn dùng. Không xoá dữ liệu — xoá đi qua §13.
9. School enrollment
school_enrollments.status — migration 0003: active → paused → left. Từ paused quay lại active được; left là cuối. Chỉ enrollment active mới sinh goal trong Goal Model.
10. Mentor assignment
mentor_assignments.status — migration 0008: active ⇄ ended. Không phải điều kiện truy cập (SRC-037): mentor xem được mọi learner bất kể có assignment hay không; bảng này chỉ nói ai theo dõi chính. Xem permissions.
Phần C — Quyền riêng tư & dữ liệu
11. Consent
consents.granted (0/1) + granted_at / revoked_at — migration 0033. Không có cột status; trạng thái là cặp granted + thời điểm.
(chưa hỏi: không có dòng)
│ phụ huynh bấm đồng ý
▼
granted = 1, granted_at = now, policy_version = bản đang hiệu lực
│ phụ huynh rút lại
▼
granted = 0, revoked_at = now ──▶ đồng ý lại: granted = 1, granted_at mớiBa luật:
- Không có dòng ≠ đã từ chối. Không có dòng nghĩa là chưa hỏi — tính năng của scope đó không được chạy, y như bị từ chối, nhưng giao diện phải hỏi chứ không được báo "bạn đã từ chối".
- Rút lại không xoá dòng.
granted=0+revoked_at; bảng này là bằng chứng pháp lý. - Đổi chính sách không tự động huỷ đồng thuận cũ.
policy_versiongiữ nguyên bản lúc bấm; muốn áp bản mới thì phải hỏi lại.
7 scope: account, learning_data (bắt buộc — không có thì không có tài khoản), ai_processing, portrait, community, mentor_access, research.
12. Data deletion request
data_deletion_requests.status — migration 0033.
pending ──▶ in_progress ──▶ completed
└────────────────────▶ rejected (kèm lý do trong `note`)completed bắt buộc điền note: đã xoá gì, giữ lại gì, vì sao. Một yêu cầu xoá kết thúc mà không nói được đã làm gì thì về mặt bằng chứng là chưa làm.
13. Retention policy
retention_policies không có trạng thái — là bảng khai báo. Nêu ở đây để trả lời dứt điểm: không có vòng đời, sửa là sửa thẳng, lịch sử nằm ở git của migration.
Phần D — Nội dung
14. Item — vòng đời một câu hỏi
items.status — migration 0032. Đây là trạng thái quan trọng nhất của khu nội dung.
candidate ──┬──▶ published ──▶ retired
└──▶ (ở nguyên candidate nếu không đạt ngưỡng chất lượng)| Giá trị | Nghĩa | Learner có thấy không |
|---|---|---|
candidate | Đã sinh/đã sửa, chờ đạt ngưỡng đánh giá | ❌ |
published | Đang phục vụ learner | ✅ |
retired | Gỡ khỏi luồng, giữ lịch sử để bài đã làm vẫn giải thích được | ❌ |
Mặc định của cột là published — có chủ đích, để 2.146 item đang chạy không biến mất khỏi Phòng Lab ngay sau migration. Item mới do AI sinh luôn bắt đầu ở candidate (AS-10.4.1).
retired không quay lại published: hồi sinh nội dung là tạo version mới và publish version đó (§15).
15. Item version — vì sao không sửa trực tiếp
item_versions.status — migration 0032.
candidate ──┬──▶ published ──▶ superseded (version mới hơn được publish)
└──▶ rejected (người soát bác — giữ lại để biết đã bác gì)- Mỗi thay đổi nội dung tạo version mới, không sửa bản đang chạy (AS-06.3.1 🔴).
- Publish là đổi con trỏ, không phải ghi đè: đúng một version
publishedcho mỗiitem_id. - Rollback = publish lại một version cũ, không sửa tay DB. Đây là lý do
supersededphải bất biến. rejectedlà nhánh cuối; muốn dùng lại ý tưởng đó thì tạo version mới.
16. Content review queue — báo sai thì đi đâu
content_review_queue.status — migration 0032.
open ──▶ in_review ──┬──▶ resolved (đã sửa — kèm `resolution`)
└──▶ dismissed (không phải lỗi — vẫn kèm `resolution`)Hai nguồn đổ vào: người báo (learner/phụ huynh, qua content_reports) và máy sàng lọc (items.screen_flags_json, reported_by IS NULL). Cả hai nhánh kết đều bắt buộc ghi resolution — đó là điều biến "learner báo sai" thành một vòng khép kín thay vì rơi vào hư vô (AS-10.4.5).
17. Content report
content_reports.status — migration 0006: open → reviewed | dismissed | actioned. Là sổ nhận báo cáo của khu tương tác; việc xử lý nội dung học đi tiếp sang content_review_queue (§16).
18. Learning experience
learning_experiences.status — migration 0019: draft → review → published → deprecated. Experience do AI sinh vào draft, không tới learner cho tới khi người soát duyệt.
19. Content blueprint
content_blueprints.status — migration 0016: draft → active → deprecated. Blueprint deprecated không sinh nội dung mới nhưng nội dung đã sinh từ nó vẫn sống.
20. Generation run
generation_runs.status — migration 0016.
queued ──▶ running ──┬──▶ review ──▶ done
└──▶ failedreview là trạng thái riêng có chủ đích: lô đã sinh xong về mặt kỹ thuật nhưng chưa ai nhìn. Không có review thì done sẽ nói dối.
21. Lab
labs.status — migration 0037: draft → live → retired. Mặc định live.
22. Exam — đề thi
exams.status — migration 0004: draft → published → archived.
Đề thi thật đã publish là bất biến (QG-011): sửa nội dung đề thật = tạo đề mới, không sửa tại chỗ. source phân biệt generated (hệ thống soạn) với đề có nguồn thật; archived giữ để bài làm cũ vẫn chấm lại được.
23. Exam attempt & assessment session
Hai bảng, cùng hình dạng — exam_attempts.status (0004) và assessment_sessions.status (0002):
in_progress ──┬──▶ completed
└──▶ abandoned (bỏ dở, không tính vào bằng chứng có độ tin cao)abandoned không bị xoá: bỏ dở giữa chừng cũng là một tín hiệu về độ khó và độ dài bài.
24. Learner experience state
learner_experience_state.status — migration 0028: chỉ hai giá trị, completed | failed.
Không có in_progress — có chủ đích: đang làm dở là trạng thái ở client, ghi vào D1 sẽ đẻ ra hàng loạt hàng rác mỗi lần learner đóng tab. failed = Học vượt (skip) sai câu nên trượt lượt đó.
Khoá: (learner_id, subject_id, unit_key, exp_key). exp_key ∈ skip | mid | final | explore:<node> | practice:<node> | practice<n>:<node>.
Phần E — Learner Intelligence
25. Model version
(mới) ──▶ active ──▶ supersededChỉ có một active cho mỗi (learner_id, model_kind). Version mới chỉ sinh khi content_hash đổi — engine chạy ra kết quả y hệt thì không đẻ version, nhưng vẫn có dòng engine_runs (§27). Đã superseded thì bất biến vĩnh viễn; đó là điều làm cho việc "xem lại hôm đó hệ thống nghĩ gì" trở nên khả thi.
26. Goal
Horizon (theo số ngày còn lại):
≤ 14 ngày → operational ≤ 90 → tactical > 90 → strategic không deadline → undatedStatus:
| Bảng | Giá trị | Ghi chú |
|---|---|---|
learner_goals | active | achieved | dropped | Goal mức tổng |
learner_goal_entries | active | achieved | dropped | Từng mục tiêu do Goal Engine tái sinh từ origin_kind/origin_id |
learner_exam_targets | active | dropped | Không có achieved — trường mục tiêu thì hoặc còn theo đuổi, hoặc bỏ; "đỗ rồi" là sự kiện ngoài hệ |
Goal biến mất khỏi Goal Model → dropped (không xoá, giữ lịch sử). Xuất hiện lại → active. Khoá idempotent (learner_id, origin_kind, origin_id) giữ cho engine chạy lại không đẻ trùng.
Conflict flags: deadline_passed · time_competition. Engine chỉ gắn cờ, không tự giải quyết.
27. Learner context event — lời khai bối cảnh
learner_context_events.status — migration 0030.
active ──┬──▶ expired (quá effective_to)
└──▶ superseded (lời khai mới cùng loại — superseded_by trỏ tới bản mới)Lời khai mới không ghi đè lời cũ. Đây là lý do Planning Engine giải thích được "vì sao tuần trước kế hoạch nhẹ hơn": lời khai "con đang ốm" vẫn còn nguyên ở trạng thái superseded.
kind ∈ time_budget, illness, exam_soon, busy, stress, motivation, focus_subject, other. source_role ∈ learner | parent.
Phần F — Vận hành
28. Workflow run
workflow_runs.status — migration 0027.
running ──┬──▶ succeeded
└──▶ failedworkflow_run_steps.status có thêm skipped: succeeded | failed | skipped. Một run có thể failed mà vẫn có step succeeded — xử lý lỗi từng phần.
Run kẹt ở running quá lâu = worker chết giữa chừng. Đó là tín hiệu vận hành thật, không phải giá trị rác — đừng dọn nó bằng cách ép về failed mà không ghi lý do.
29. Engine run
engine_runs.status — migration 0027: running → succeeded | failed.
Cột đi kèm mang nghĩa mà status không nói được:
| Cột | Nghĩa |
|---|---|
produced_change | 0 = engine chạy xong, kết quả không đổi nên không sinh version mới. Đây là kết quả thành công, không phải hỏng. |
produced_model_kind / produced_version | Version đã sinh, nối sang learner_model_versions (§25) |
workflow_run_id | Engine chạy trong khuôn khổ workflow nào; NULL = chạy lẻ |
30. Queue dead letter
queue_dead_letters.replay_status — migration 0036. Cột cho phép NULL, và NULL có nghĩa riêng.
NULL (vừa rơi vào DLQ, chưa ai xử lý)
│
├──▶ pending (đã xếp hàng chờ replay)
├──▶ replayed (đã chạy lại thành công — replayed_at được điền)
├──▶ failed (chạy lại vẫn hỏng)
└──▶ discarded (quyết định bỏ — message không còn ý nghĩa, vd learner đã bị xoá)Message vào đây sau max_retries = 5 lần thất bại. payload_json giữ nguyên văn để replay đúng như cũ; INSERT OR IGNORE theo (queue_name, message_id) nên cùng một message chỉ nằm một dòng. Xem queues.
31. Rate limit bucket
rate_limit_buckets không có trạng thái — (key, window_start, count). window_start đổi thì count reset về 1. Nêu ở đây để không ai đi tìm state machine của nó.
Phần G — Tương tác & chân dung
32. Interaction & forum topic
| Bảng | Giá trị | Ghi chú |
|---|---|---|
interactions | visible → hidden → blocked | blocked là cuối; nội dung giữ lại để điều tra, không xoá |
forum_topics | open → hidden | locked | locked = còn đọc được, không trả lời thêm |
Nội dung bị report vào hàng chờ kiểm duyệt (§17); media phải duyệt trước khi public (QG-008).
33. Portrait & nhánh liên quan
| Bảng | Giá trị |
|---|---|
portraits | active → archived |
portrait_tracks | planned → active → paused → done | dropped |
milestones | planned → in_progress → achieved | missed | rescheduled |
portrait_reactions không có status — giá trị phản ứng là agree · unsure · disagree. Ba giá trị này không đổi hành vi Recommendation Engine: chúng là tiếng nói của đứa trẻ trong cuộc trò chuyện gia đình, không phải tín hiệu điều khiển hệ thống.
portrait_learner_sections cũng không có status — nội dung con viết chỉ có "đã viết" hoặc chưa; quyền ghi giới hạn ở role learner (AS-07.3.3).
34. Orca — thi đấu
| Bảng | Giá trị |
|---|---|
competitions | active (mặc định) — không có CHECK, tập giá trị chưa chốt |
competition_editions | upcoming (mặc định) — không có CHECK |
Ghi thẳng khoảng trống: hai cột này chưa có ràng buộc DB, nên bất kỳ chuỗi nào cũng ghi vào được. Cần thêm CHECK khi Orca ra khỏi giai đoạn thử.
35. Bắt buộc khi thêm trạng thái mới
CHECK (status IN (...))trong migration — không có CHECK thì cột không phải trạng thái, nó là ô ghi chú.- Thêm mục vào trang này với đủ: giá trị, mũi tên chuyển tiếp, ai được đổi, và nhánh nào là cuối.
- Nói rõ giá trị mặc định và vì sao chọn giá trị đó (mặc định sai làm dữ liệu cũ đổi nghĩa im lặng).
- Nếu có giá trị mang nghĩa "chưa ai xử lý" (NULL,
open,queued) → nói rõ nó khác gì với "đã xử lý và kết luận là không làm gì". - Nghĩa nghiệp vụ của bảng đó → data-semantics.
Trace
- REQ-UX-03 (màu trạng thái), REQ-INT-28 (nhãn trí nhớ), REQ-INT-29 (model version), REQ-SEC-02, REQ-DOC-04.
- Thiết kế: SDD-002, SDD-003, SDD-005, SDD-006, SDD-013, SDD-017.
- Trang anh em: Data Semantics, Data Dictionary.
- Kiểm chứng: QG-004.