SDD-038 · Màn làm bài, Grammar và khu đề
Một phần của SDD-038 Cổng IELTS. Phần đầu là đặc tả đang áp dụng, mỗi mục giữ số § cũ; diễn biến và lý do các vòng quyết định nằm ở mục Lịch sử quyết định cuối trang.
29. Giọng của learner: lưu lại, nghe lại được, và có hạn xoá (SRC-884, 2026-09-20)
a. Mạch lưu-phiên âm-chấm đã chạy từ SRC-801, nhưng chỉ ở một chỗ
Đường /ielts/speaking/levels/{1,2,3} đã làm đủ: ghi âm trên trình duyệt, đẩy tệp thô vào R2 (CONTENT.put(audioKey(...))), ghi audio_key + audio_seconds vào ielts_productions, phiên âm bằng @cf/openai/whisper-large-v3-turbo, rồi chấm theo rubric của bậc. Nói cách khác, phần "AI bóc tách transcript rồi chấm điểm" không phải việc làm sau — nó đang chạy production.
Chỗ thiếu nằm ở sáu năng lực nhỏ của Speaking. Chúng luyện bằng câu hỏi bốn lựa chọn, y hệt Reading: learner đọc bốn phương án rồi bấm một cái, và cả buổi "luyện nói" trôi qua mà không ai mở miệng. Đó chính là màn hình chủ dự án đang nhìn.
b. Bốn câu trắc nghiệm ở lại, và mọc thêm một bước cuối
Dễ nhất là kết luận rằng luyện nói bằng trắc nghiệm là sai rồi thay sạch bằng ghi âm. Nhưng bốn câu ấy làm một việc mà ghi âm không làm được: chúng bày ra bốn cách xử lý cùng một tình huống rồi hỏi cách nào tốt hơn. Learner chưa từng thấy ba cách còn lại thì không có gì để so, và một đoạn ghi âm không dạy nó rằng im lặng tìm từ tệ hơn nói vòng.
Nhận ra là bước trước; làm được là bước sau. Khoảng cách giữa hai bước ấy chỉ đóng lại bằng cách nói. Nên bộ bài giữ nguyên và có thêm một bước ở cuối, sau hàng nút "Làm lại / Chọn bài khác": đề nói đứng ngay sau bốn lời giải vừa đọc, và learner mang chúng vào đoạn nói của mình.
Đặt ở đầu thì thành mời learner làm đúng cái nó chưa học; chen lên trước hàng nút thì thành một cái cửa quay chắn ngang đường ra của người đã làm xong bài.
c. Đề nói nằm ở CSDL, khác với đề của thang ba bậc
Đề của thang ba bậc nằm trong mã nguồn (modules/ielts/ladder.ts) vì chúng là kiến thức về một kỳ thi ổn định nhiều năm. Đề ở đây thì buộc vào một năng lực nhỏ cụ thể, và chính các năng lực ấy đã sống trong CSDL và sửa được ở màn admin từ SRC-805. Để đề nói trong mã nguồn nghĩa là người soạn sửa được tên năng lực, sửa được bốn bài tập của nó, nhưng phải mở một pull request để sửa đúng một câu hỏi quan trọng nhất.
Migration 0256 thêm speak_prompt_en + speak_prompt_vi vào ielts_micro_skills và seed sáu đề. NULL nghĩa là năng lực ấy không có bước nói, và màn kết đơn giản không hiện khối ấy — Reading và Listening luôn rơi vào nhánh này.
Bài nộp vào ielts_productions với track='micro' (bên cạnh 'diagnostic' của SRC-792 và 'practice' của SRC-801) và prompt_id dạng micro:speaking.<code>, nên truy ngược từ một bài nộp về năng lực nó thuộc về là một phép cắt chuỗi chứ không phải một cột thứ hai.
Rubric là RUBRICS[2] cho mọi lượt nói ở đây, và cố ý không có band: lượt này dài ba bốn chục giây và soi vào đúng một thao tác. Bậc 3 sẽ gắn một con số band vào bốn mươi giây, và một band dựng từ bốn mươi giây là con số nghe như phép đo mà không phải. Ai muốn band thì đã có thang ba bậc, nơi đề dài hơn và rubric nói thẳng rằng nó đang chấm band.
Sàn 15 giây, trần 90 giây — hẹp hơn hẳn 20-240 của thang bậc, vì đây là một việc ba mươi giây chứ không phải một bài thi, và vì trần giây cũng là trần hoá đơn Whisper.
d. Learner nghe lại được chính mình
Đây là chỗ thiếu lớn hơn cả bước nói, và nó đã tồn tại lặng lẽ từ SRC-792: tệp vào R2 rồi chỉ mentor mở lại được (modules/mentor/routes.ts). Learner nộp xong nhận về một bản phiên âm và mấy câu nhận xét; giọng của chính nó nằm trong một cái bucket nó không có đường tới.
Bản phiên âm không thay được bản ghi, vì nó đã lọc mất đúng những thứ đang luyện. Bốn trong sáu năng lực nhỏ của Speaking là pace, self-correct, paraphrase, long-turn — cả bốn sống ở chỗ chữ viết không giữ lại. Một learner đọc "I think, er, the main reason is" thì thấy một câu bình thường; nghe lại chính mình thì nghe thấy ba giây im lặng.
Trang /ielts/recordings gộp cả ba track vào một danh sách theo thời gian. Learner không biết tới ba chữ ấy và cũng không cần: với nó, đây chỉ là "những lần con đã nói". Lọc theo track sẽ giấu mất chính bài chẩn đoán, tức là bài nói đầu tiên và thường là bài đáng nghe lại nhất.
Phát qua worker, không ký URL R2 — cùng lý do đã ghi ở đường của mentor: một URL đã ký thì sống tiếp sau khi phiên hết hạn, sau khi gia đình rời đi, sau khi ai đó dán nó vào một tin nhắn. learner_id nằm trong mệnh đề WHERE chứ không phải một phép kiểm sau khi đã đọc xong dòng, nên không có nhánh nào để quên.
e. Luật xoá sinh ra cùng lúc với luật lưu
Một tính năng lưu giọng trẻ em mà không có đường xoá là một rủi ro vĩnh viễn đổi lấy một công dụng không ai kể ra được. Lượt dọn hằng đêm (audioRetention.ts, gắn vào cron 0 21 * * *) xoá tệp quá 180 ngày, trần 500 tệp một lượt.
Cái bị xoá là tệp, không phải bài nộp: bản phiên âm, nhận xét, band mentor chốt và ngày nộp ở lại nguyên vẹn; chỉ audio_key về NULL. Learner mất khả năng nghe lại một bài rất cũ, chứ không mất chính bài ấy — đánh đổi đúng chiều, vì thứ bị bỏ là thứ ít người dùng nhất và rủi ro nhất.
Thứ tự R2 trước, D1 sau là cố ý. Hỏng giữa chừng theo thứ tự này để lại một khoá trỏ vào chỗ trống, và lượt sau dọn nốt. Thứ tự ngược lại để lại một tệp mồ côi: không dòng nào trỏ tới nó nữa, nên không lượt dọn nào tìm ra nó nữa.
Con số 180 ngày được nói thẳng ra trên chính trang nghe lại, không giấu trong trang điều khoản: một learner quay lại sau một năm và không thấy bài cũ sẽ nghĩ hệ làm mất bài của nó.
e-bis. Nghe được nhưng không tua được (sửa 20.09.2026)
Nguyên nhân: route trả thẳng obj.body, không có Accept-Ranges, và một request Range: bytes=1000-2000 được trả lời bằng 200 kèm trọn 429.200 byte thay vì 206. Trình duyệt đọc điều đó là "máy chủ không cắt khúc được" rồi bỏ hẳn thanh tua.
Nay cả hai đường (learner và mentor) dùng chung shared/audioRange.ts. Đường của mentor mang y nguyên lỗi này từ SRC-792: thầy cô nghe một đoạn ba phút cũng không tua được, và chép cách sửa sang hai chỗ là chờ ngày chúng lệch nhau.
Bản giả R2 trong testkit cũng phải biết cắt khúc — một bản giả không cắt khúc sẽ làm test xanh trong khi production trả nguyên tệp, tức là dựng lại đúng cái lỗi vừa sửa.
f. Còn mở
/ielts/recordingslọc bỏ dòng đã hết hạn tệp (audio_key IS NOT NULL), nên bài cũ biến khỏi danh sách thay vì hiện ra kèm câu "tệp đã xoá, bản chữ còn đây". Bản chữ vẫn nằm trong CSDL và mentor vẫn đọc được; chỉ learner là mất đường tới. Đó là hành vi hiện tại, đã ghi trong test, chứ không phải điều đáng mong muốn.- Phụ huynh không có đường nghe bài của con (chốt 2026-09-20: learner + mentor). Mở thêm vai này cần một phép kiểm quan hệ phụ huynh-learner ở route, chưa làm.
- Sáu đề nói hiện do migration seed. Màn admin đã sửa được
ielts_micro_skillsnhưng chưa có ô nhập cho hai cột mới, nên sửa đề vẫn phải qua SQL.
89. Bảng rộng làm cả trang cuộn ngang (SRC-987, 22.09.2026)
Nguyên nhân là <fieldset>, không phải cái bảng. UA stylesheet của trình duyệt đặt min-inline-size: min-content cho fieldset, nên nó KHÔNG chịu hẹp hơn nội dung - kể cả khi nội dung ấy nằm trong một khung overflow-x-auto lẽ ra phải tự cuộn. EditGate bọc mọi form trong một fieldset disabled (đó là cách chế độ đọc khoá mọi ô mà không phải nhớ ô nào), nên mọi trang dùng EditGate đều mang sẵn cái bẫy này. Sửa bằng min-w-0 trên chính fieldset.
Ca e2e đo document.scrollWidth sau khi nới thêm năm tháng, ở cả 1280px lẫn 375px, và đo ở cả chế độ sửa lẫn chế độ đọc - fieldset đổi lớp giữa hai chế độ.
91. Lối ra khỏi bài đang làm, và nhãn nút bằng tiếng Anh (SRC-993, 23.09.2026)
Chỉ đạo: nút dùng icon và tiếng Anh; bỏ nút "Chọn bài khác"; thêm một icon để dừng việc làm bài rồi quay lại màn chọn bài.
Vì sao cái dấu X đáng giá nhất trong ba việc trên. Trước bản này, learner đang làm dở một bài mà muốn đổi sang bài khác thì chỉ còn đường bấm vệt đường dẫn ở đầu trang - mà không ai coi vệt đường dẫn là một cái nút. Trên điện thoại, việc họ thật sự làm là bấm nút Back của trình duyệt, và phần đã làm mất theo. Một dấu X ngay cạnh thanh tiến độ là chỗ mắt tìm tới khi muốn dừng, và nó có mặt SUỐT cả bài chứ không chỉ ở màn cuối.
Chính vì thế nút "Chọn bài khác" ở màn cuối bỏ được: hai đường tới cùng một chỗ trên một màn là một đường thừa, và đường còn lại thì rộng hơn hẳn.
Nhãn nút nói tiếng Anh (Try again, Next exercise) theo luật sân luyện SRC-834. Câu ghi nhận ngay phía trên vẫn tiếng Việt, và đó không phải mâu thuẫn: SRC-953 vạch đúng ranh giới ấy - chữ Nemo NÓI VỚI learner thì tiếng Việt, còn nhãn của một nút là một phần của giao diện.
Cả hai nút icon đều chỉ có hình, nên cả hai mang aria-label hoặc chữ đi kèm: một nút chỉ có hình là một nút câm với người không nhìn thấy nó. Ca e2e gác đúng chỗ ấy - nó tìm nút dừng bằng chính cái nhãn đọc-thành-lời.
96. Màn làm bài: bớt cam, và chữ giao diện nói tiếng Anh (SRC-1008, 23.09.2026)
Bớt cam. Trước bản này, một câu vừa chấm đúng có tới bốn mảng cam cùng lúc: viền ô đáp án, nền ô đáp án, dấu tích, và chữ "Đúng rồi!" to ngay bên dưới - cộng thêm thanh tiến độ cam ở đầu trang. Một màn hình mà màu nhấn chiếm cả nửa thì màu nhấn không còn nhấn được gì. Nay ô đúng và thanh tiến độ dùng màu biển; cam chỉ còn ở ĐÚNG MỘT chỗ - dấu tích cạnh đáp án đúng - nên mắt vẫn tìm ra nó ngay. Chữ "Correct!" chuyển sang màu chữ chính; những tia sáng nhỏ quanh nó vẫn giữ cam vì chúng chỉ tồn tại nửa giây.
Tiếng Anh cho mọi chữ giao diện của màn làm bài: Check, Correct!, Not quite., Next →, Finish, câu tóm tắt cuối bài, nhãn thanh tiến độ, câu nhắc bản chữ, các chữ quanh bước ghi âm.
Đây là một vạch HẸP LẠI của SRC-953, và cần nói rõ để không ai "sửa lại cho đúng luật": SRC-953 cho chữ HƯỚNG DẪN trong sân luyện nói tiếng Việt, vì một learner band 5.0 đọc lời giải thích tiếng Anh sẽ hiểu lỗ chỗ. Trên màn LÀM BÀI thì chủ dự án chọn tiếng Anh cho cả những câu ngắn của Nemo - chúng đủ ngắn để không cần dịch, và một nút tiếng Việt giữa một đề tiếng Anh là đúng cái lớp dịch mà SRC-834 gỡ đi.
Thứ KHÔNG đổi: nội dung câu hỏi. Bậc 1 và 2 của phần Nói, phần Viết, và bậc 1 của phần Nghe vẫn tiếng Việt theo thang bốn chặng (SRC-821, SRC-976) - đó là chữ trong BÀI, không phải chữ trên GIAO DIỆN. Lời giải thích (why_vi) đi theo ngôn ngữ của chính câu hỏi.
97. Màn kết bài: "Try again" chỉ còn icon, thêm "Take a break" (SRC-1009, 23.09.2026)
"Try again" chỉ còn icon. Cạnh nút chữ Next exercise, một mũi tên vòng tròn đã đủ nói "làm lại", và bỏ chữ đi làm nút chính nổi hẳn lên. aria-label và title giữ lời cho trình đọc màn hình và cho người rê chuột; ca e2e tìm nút bằng chính cái tên ấy và đồng thời đòi nút không có chữ nào hiện ra.
"Take a break" quay về danh sách bài của năng lực đang luyện. Dấu X cạnh thanh tiến độ vẫn có suốt cả bài (SRC-993), nhưng ở màn cuối thì mắt learner nằm ở hàng nút, không ở góc trên - và câu hỏi của họ lúc ấy chính là "giờ đi đâu". Nhãn nói ra VIỆC ("tạm nghỉ") chứ không nói đích đến, vì đó là điều learner đang chọn.
101. Bài luyện nhỏ vào sổ kèm thời gian làm (SRC-1013, 23.09.2026)
Chủ dự án: "Tôi vừa luyện tập rồi, nhưng ko thấy số liệu được điều chỉnh." Sổ production cho thấy mọi dòng exercise:* đều có seconds = 0: MicroExercise ghi số câu và số câu đúng nhưng không đo giờ. Dashboard hub (Am I showing up, This month, mốc tháng) cộng GIÂY, nên bài làm xong không nhích được con số nào.
- Màn bài tập tự đo thời gian nhìn (dừng khi tab ẩn, trần 90 phút) và gửi kèm CHÍNH dòng "làm xong". Không tách dòng riêng:
ielts-micro-attemptsvàmasteryđếm lượt làm theo tiền tốexercise:, một dòng thời gian mang tiền tố ấy sẽ thành lượt làm ma. - Giới hạn còn lại: thời gian của một bài bỏ dở không được ghi (chỉ ghi khi làm xong), và các dòng cũ vẫn 0 giây. Không bù số cho dòng cũ vì mọi con số bù đều là đoán.
112. Mỗi bài luyện có số phút cố định (SRC-1034, 25.09.2026)
Chủ dự án: "Mỗi bài luyện tập của các sub-skills, đều cần tương ứng với một số phút. Để khi learner học xong mỗi lần thì hệ thống lại cộng thêm thời gian đã học."
- Cột
minutes(migration 0292) = số câu một lượt x phút mỗi câu, làm tròn lên, tối thiểu 3 (Reading 1 / 1.5 / 2 theo bậc, Listening 2, Writing 1.5, Speaking 2). Sửa được từng bài. - Làm xong thì server cộng đúng
minutes x 60giây, thay cho số giây đồng hồ trên máy đo (SRC-1013). Lượt khác (đọc bài, bài chưa định phút) giữ số client gửi. - Hiện ra: thẻ bài in "· N min"; màn kết bài in "+N min added to your study time".
- Cái giá: learner bấm qua thật nhanh vẫn được cộng đủ phút. Chấp nhận theo chỉ đạo; nếu cần, thêm một mức sàn thời gian tối thiểu sau.
114. Video "What is a Learning Plan?" chỉ cho tài khoản xem thử (SRC-1043, 25.09.2026)
Chủ dự án: "Trong trang IELTS Learning Plan, tạo ra một video có nhạc, có lời bằng tiếng Anh, để chia sẻ và giải thích cho learners... lưu vào đâu đó trong Cloudflare. Về công nghệ, sử dụng remotion. Hãy làm cho riêng tài khoản của tôi, là dac2205@gmail.com."
- Dựng:
tools/videos(Remotion 4.0.528), NGOÀI workspaces vì đây là công cụ render chạy trong CI, không vào bundle của site nào, nên không đụng luật bộ ba UI (SRC-650). Tám cảnh theo tám đoạn lời (scripts-content/learning-plan.json): nỗi lo, Learning Plan, điểm hiện tại → mục tiêu, từng tháng, phút cộng dồn, lời mời. - Tiếng và nhạc: lời đọc sinh bằng Workers AI
aura-2-en(cùng model TTS với Listening), giọng asteria; độ dài từng cảnh lấy theo độ dài lời thật. Nhạc nền tự tổng hợp bằng code (không tải từ đâu về, không vướng bản quyền). - Lưu và phát: workflow chạy tay
render-videos.ymlrender rồi đưa lên R2nemo12-content/videos/learning-plan.mp4(đúng luật mọi thứ chạm production đi qua GitHub). Phát quaGET /v1/owner/videos/learning-plan: có phiên và email trong danh sách xem thử mới ra, mọi người khác nhận 404. Thẻ "Watch first" trên trang Learning Plan chỉ hiện cho email ấy. - Remotion miễn phí cho cá nhân và công ty tối đa 3 người; quá mức đó cần giấy phép công ty.
115. Nút hướng dẫn ở góc phải mọi trang (SRC-1048, 25.09.2026)
Chủ dự án: "Trong tất cả các trang, luôn có một icon gì đó ở góc trên phải, click vào đó thì nó sẽ mở ra Popover để hướng dẫn. Các hướng dẫn cần chia theo cụm, ngắn gọn... bằng tiếng Việt là chính." Nút "?" đứng cạnh ảnh đại diện ở cả hai khung header của sân IELTS (ielts/Shell.tsx và cổng /ielts trong App.tsx), mở popover shadcn. Nội dung ở ielts/helpContent.ts, chọn theo đường dẫn (trang chính, trang kỹ năng, sub-skill, bài luyện, How to do this, task type, Learning Plan, Learner Profile, Membership, Referral, Setup, Leaderboard, Live, đề luyện; còn lại dùng bản chung). Mỗi trang 2-4 cụm, mỗi cụm 1-3 dòng.
118. Năm video "một thông điệp" trong sân luyện (SRC-1052, 25.09.2026)
Gắn bằng ielts/IeltsClip.tsx: một dòng "▶ Video 1 phút", bấm mới mở, phụ đề Việt bật sẵn. Reading (skills-first), Listening (listen-twice), trang chính dưới dashboard (small-steps), Learner Profile (know-you), Membership (three-months). Kịch bản và luật dựng: docs/product/videos. Menu tài khoản có thêm mục "What's new" trỏ sang trang công khai (SDD-029).
122. Khu ngữ pháp nền tảng (SRC-1065, 26.09.2026)
Chủ dự án: "cần có một khu vực chuyên để học ngữ pháp... 6 mảng, mỗi mảng 6 chủ đề ngữ pháp nền tảng nhất... video giới thiệu... một đoạn giới thiệu, 3 ví dụ, và 3 ví dụ dễ gây hiểu lầm... 5 Practice Tasks... từ level 1 dễ nhất tới level 5 khó nhất."
- URL:
/ielts/grammar(6 mảng: Verb tenses, Building sentences, Nouns and determiners, Adjectives and adverbs, Modals and verb patterns, Linking ideas) và/ielts/grammar/<chủ đề>. Lối vào: thẻ "Build your base" ở trang/ielts. - Nội dung là 36 file JSON trong bundle (
apps/learn/src/ielts/grammar/content/), như tầng luyện đọc: mở được khi API hỏng, không cần migration. Soạn bằng một workflow 12 agent (một người soạn và một người duyệt cho mỗi mảng); lượt duyệt sửa 11 chỗ, phần lớn là câu có hơn một đáp án đúng. - Bài tập: level 1-3 chọn đáp án, level 4-5 điền từ (có danh sách đáp án chấp nhận), level 5 mang giọng câu học thuật IELTS.
- Ngôn ngữ theo SRC-953: tên trang, tên chủ đề, đề bài tiếng Anh; giới thiệu, ghi chú, lý do lỗi, giải thích đáp án tiếng Việt.
- Video:
grammar-<chủ đề>, cùng khuôn "một thông điệp" (luật, một ví dụ, một lỗi hay gặp, lời mời luyện), phụ đề Việt/Anh; render bằng workflow render-videos. Video chưa render thì dòng video tự ẩn. - Tiến độ và phút học: xem §123.
123. Grammar vào sổ học tập, cộng phút học (SRC-1066, 26.09.2026)
- Migration 0299 nới CHECK của
learning_events.skillthêm'grammar'(dựng lại bảng như 0249). Một sổ duy nhất nên phút học, chuỗi ngày, bảng xếp hạng tuần, thưởng học tập và dự đoán 200 giờ tự cộng bài ngữ pháp mà không sửa chỗ nào. POST /v1/learners/{id}/ielts-grammar {topic, level}: bài làm ĐÚNG lần đầu ghi một dòng, cộng cố định 120 giây. Làm lại bài đã đúng không cộng nữa (INSERT ... WHERE NOT EXISTS), nếu không bấm Kiểm tra nhiều lần là nhiều lần hai phút.GETtrả danh sách bài đã đúng và tổng phút.- Dòng ngữ pháp ghi
items = 0: là công sức, không phải bằng chứng về bốn kỹ năng, nên không vào tỉ lệ đúng, lời khen "bài đọc trọn vẹn đầu tiên", hay learner_evidence. - Dashboard: thẻ "Build your base" ở
/ieltshiệnn/180; trang Grammar đọc tiến độ từ máy chủ (gộp với bản localStorage khi chưa đăng nhập).
124. Grammar hiện dần (SRC-1067, 26.09.2026)
Chủ dự án: "Mặc định khi mới vào thì đừng hiển thị quá nhiều thông tin... hiển thị cực kỳ ít thông tin (kiểu như vài thông tin tổng quan), và chỗ để learners bắt đầu. Learner cần gì thì mới click vào chỗ đó để hiển thị dần ra." Ảnh kèm là trang chủ đề, nơi video chiếm trọn màn đầu.
- Trang chủ đề: mặc định chỉ tên, một dòng công thức, tiến độ
x/5và MỘT nút bắt đầu. Video, "Chủ đề này là gì", Examples, Common mistakes là bốn dòng gập, bấm mới mở. Bài tập hiện TỪNG bài, đúng rồi mới sang bài sau; xong 5/5 thì mời sang chủ đề kế. - Trang Grammar: tổng
n/180, một nút "Bắt đầu/Học tiếp" trỏ chủ đề chưa xong đầu tiên, và sáu mảng gập lại, bấm mới hiện sáu chủ đề. - SRC-1068 (26.09.2026): "Phần Practice, mỗi thời điểm chỉ hiển thị đúng 1 câu. Và khi đã practice thì các phần khác sẽ được ẩn bớt đi." Bấm bắt đầu thì công thức và bốn cụm gập ẩn hẳn, màn chỉ còn tên chủ đề và MỘT câu; một dòng "Dừng luyện, xem lại lý thuyết" đưa chúng trở lại.
127. Dấu "?" giải thích thuật ngữ ở tiêu đề sub-skill và dạng đề (SRC-1082, 27.09.2026)
Chủ dự án: cạnh tiêu đề (ví dụ "Inference") là dấu hỏi, bấm vào ra popover giải thích và cho ví dụ về chính thuật ngữ ấy bằng tiếng Việt, cho mọi trang; bỏ câu "Luyện cho vững sub-skill này trước; dạng bài thi (task type) sẽ tới sau." (SRC-1050), thay bằng một dòng tiếng Anh ngắn bổ nghĩa cho tiêu đề.
TermTitle(apps/learn/src/ielts/TermTitle.tsx) dùng cho 51 trang sub-skill (/ielts/skills/...) và 12 trang dạng đề Reading (/ielts/tasks/reading/...).- Nội dung ở
apps/learn/src/ielts/data/termHints/*.json, khoá<micro|task>:<kỹ năng>:<mã>:tagline_en(3-8 từ),what_vi,example_vi. Soạn bằng workflow 10 agent (soạn + duyệt theo nhóm kỹ năng); lượt duyệt sửa 6 chỗ, gồm hai câu nói sai tiêu chí chấm Speaking và một câu hứa quá tay về band 6 ở Writing. - Mã mới chưa có nội dung thì trang chỉ hiện tiêu đề, không vỡ.
151. Khu làm đề đầy đủ: 50 đề mở khoá tuần tự, phiếu kết quả, nhận xét AI (SRC-1146, 30.09.2026)
Chủ dự án 30.09.2026: learn.nemo12.com/ielts/tests là khu làm đề đầy đủ bốn kỹ năng, 50 đề. Learner bắt đầu từ đề 1; đề k+1 chỉ mở khi đề k có một lượt ĐÃ NỘP và HỢP LỆ (làm nghiêm túc). Làm xong có phiếu kết quả theo kỹ năng lớn và năng lực nhỏ, 3 tới 5 năng lực nên ưu tiên, và vài câu nhận xét chung do AI viết.
Nội dung. Đề k ghép từ đề thứ k của mỗi kỹ năng (cùng thứ tự trang danh sách đề từng kỹ năng, SRC-930). Kho ngày 20.09.2026 ghép được khoảng 19-20 đề đủ bốn phần; số ô còn lại in "Coming soon", không bịa đề. Trang tự đếm lúc chạy nên tự mở thêm ô khi kho dày lên.
Ai quyết cái gì. App chỉ ghép đề và gửi câu đã chọn. Server (workers/api/src/modules/ieltsFullTests/) đóng dấu giờ mở và nộp từng phần bằng đồng hồ server, chấm Listening/Reading bằng đáp án trong D1, chấm Writing/Speaking theo tiêu chí bằng ielts.production-feedback@2 trên chính các bài đã nộp ở trang từng task trong khoảng giờ của phần ấy, rồi quyết hợp lệ bằng hàm thuần validity.ts.
Ngưỡng hợp lệ (THRESHOLDS trong validity.ts):
| Luật | Ngưỡng | Lý do |
|---|---|---|
| Đủ bốn phần | 4/4 đã nộp | một đề chỉ tính khi đủ |
| Câu khách quan đã trả lời | >= 90% mỗi phần L/R | bỏ trống nhiều thì điểm không nói gì |
| Thời gian tối thiểu | L/R >= 40%, W/S >= 30% thời gian chính thức (L 12, R 24, W 18, S ~4 phút) | nhanh hơn thì không kịp đọc/nghĩ |
| Nhịp trả lời | trung bình >= 15 giây/câu | điền cho xong |
| Đoán bừa | > 80% câu trắc nghiệm một đáp án cùng một vị trí (xét khi >= 10 câu) | kiểu chọn A-A-A |
| Writing | Task 1 >= 120 từ, Task 2 >= 200 từ (80% của 150/250); cụm bốn từ trùng đề <= 50% | ngắn hoặc chép đề |
| Speaking | đủ các phần, mỗi phần >= 20 giây, tổng >= 180 giây | không có gì để nghe |
| Rời tab | > 8 lần: chỉ cảnh báo, không bao giờ tự làm hỏng lượt | màn hình tắt, cuộc gọi đến cũng làm trang ẩn |
Lượt không hợp lệ vẫn có phiếu kết quả; đề sau vẫn khoá; phiếu liệt kê từng lý do bằng tiếng Việt và mời làm lại. Admin mở tay được một đề cho một learner (tab "Mở đề IELTS" ở admin, POST /v1/admin/ielts-full-tests/unlocks), mỗi lần ghi audit_log (ielts_full_test.unlock, kèm lý do). Trước migration 0316 không có lượt làm đề đầy đủ nào được lưu kèm mốc giờ và câu trả lời, nên không có lượt cũ nào chứng minh được là hợp lệ: không "grandfather", mọi learner bắt đầu từ đề 1.
Phiếu kết quả (/ielts/tests/<n>/report/<mã lượt>): band ước tính tổng (trung bình bốn kỹ năng, làm tròn nửa bậc) và từng kỹ năng, câu nói thật bằng tiếng Việt rằng đây KHÔNG phải band IELTS chính thức; mỗi kỹ năng một câu nhận xét dựng từ số liệu và bảng năng lực nhỏ (L/R theo mã năng lực của câu hỏi; Writing TR/CC/LR/GRA; Speaking FC/LR/GRA, Pronunciation "Not measured") với nhãn Strong / Developing / Needs work. Ưu tiên: 3-5 năng lực xếp theo độ yếu × trọng số (phần điểm trong kỹ năng), bỏ dòng dưới 2 câu, mỗi dòng dẫn tới trang năng lực nhỏ.
Nhận xét chung (ielts.full-test-comment@1, @cf/meta/llama-3.3-70b-instruct-fp8-fast): sinh MỘT lần mỗi lượt, lưu ielts_full_test_comments (PK attempt_id, INSERT OR IGNORE rồi đọc lại), không sinh lại. Model chỉ thấy số liệu đã tính. Mã chặn sau model: câu có con số không nằm trong đầu vào, chữ hứa hẹn điểm thi, hay dấu gạch dài thì dùng khuôn. Chạm AI_DAILY_USD_CAP hoặc AI hỏng cũng dùng khuôn. Chi phí ước tính: nhận xét ~$0,0005; chấm tiêu chí 2 task Writing + 3 phần Speaking ~$0,009; tổng ~$0,01 mỗi phiếu, cộng vào sổ ngày của trần USD.
Ngôn ngữ: tên trang, tên kỹ năng, nhãn trạng thái tiếng Anh; dặn dò, lý do, tóm tắt, nhãn nút tiếng Việt (SRC-953), xưng "bạn" như phần còn lại của sân luyện (khu này có cả teen và người lớn). Mọi mã năng lực đều in thành nhãn đọc được ("Main idea"), không in slug.
Kiểm chứng: workers/api/src/modules/ieltsFullTests/{validity,report,routes}.test.ts (từng luật hợp lệ, thứ tự mở khoá, chấm L/R bằng đáp án D1, phiếu, ưu tiên, nhận xét sinh một lần + khuôn khi AI hỏng hoặc bịa số, admin mở tay + quyền + audit), apps/learn/e2e/ieltsFullTests.spec.ts (bốn trạng thái ô, lý do không hợp lệ, các phần của phiếu, đường dẫn ưu tiên); mobileAudit.spec.ts phủ /ielts/tests, /ielts/tests/1, trang phiếu.
Lịch sử quyết định
Từ mục 29. Giọng của learner: lưu lại, nghe lại được, và có hạn xoá (SRC-884, 2026-09-20)
Chủ dự án nhìn màn hình cuối một bộ bài Speaking và viết: "Cần có tính năng để lưu lại audio của learner chứ. Tính năng 'AI bóc tách transcript rồi chấm điểm' sẽ được làm sau. Nhưng dự kiến là có tính năng đó."
Câu ấy đúng về cái thiếu và lệch về cái đã có, nên §29 phải nói rõ cả hai.
Sau khi §29 lên production, đo lại trên trang thật thì đường phát trả 200, đúng byte, readyState lên 4, duration đúng 26,58 giây. Mọi phép kiểm xanh. Nhưng audio.seekable.end(0) bằng 0: đặt currentTime = 20 thì con trỏ nhảy về 1,1 giây. Learner nghe được mà không tua được.
Đây là một LOẠI lỗi thứ ba, khác cả hai loại đã gặp hôm nay: không phải sai quyền, không phải sai vai người đọc, mà là đúng mọi thứ được kiểm, sai đúng thứ không ai kiểm. Test kiểm status và kiểm byte — cả hai vẫn đúng. e2e không chạm thẻ <audio>. Mắt nhìn màn hình cũng không thấy, vì thanh tua VẪN VẼ RA, chỉ là kéo không ăn.
SRC-885 vừa mở Content-Range/Accept-Ranges qua CORS cho các đường giọng đọc. Việc ấy cần, nhưng nó chỉ mở cửa cho những header mà máy chủ CÓ SINH RA — và đường này chưa sinh ra cái nào.
Từ mục 89. Bảng rộng làm cả trang cuộn ngang (SRC-987, 22.09.2026)
Chủ dự án nới kế hoạch lên 13 tháng và cả trang trôi sang trái: thanh bên, tiêu đề và nút "Let Nemo arrange it" bị cắt ngoài mép màn, có một thanh cuộn ngang chạy suốt chân trang.
Vì sao nó lọt qua tới tận production. Lỗi chỉ xuất hiện khi nội dung đủ rộng: mười cột thì vừa khung, mười một thì không. Trước SRC-984 learner không đổi được số tháng nên bảng gần như luôn dưới mười cột - tức là chính tính năng vừa thêm đã mở ra cái bẫy có sẵn. Đây là dạng hỏng DS-001 §4b gọi tên: không có lỗi nào nổ ra, chỉ là bố cục lặng lẽ sai ở nhà người dùng.
Từ mục 96. Màn làm bài: bớt cam, và chữ giao diện nói tiếng Anh (SRC-1008, 23.09.2026)
Chỉ đạo: "Hơi bị nhiều màu da cam quá. Và cần xài tiếng Anh thay vì tiếng Việt, không chỉ ở chỗ 'Đúng rồi' hay 'Tiếp', mà còn ở các chỗ khác nữa."
Từ mục 97. Màn kết bài: "Try again" chỉ còn icon, thêm "Take a break" (SRC-1009, 23.09.2026)
Chỉ đạo: không dùng tiếng Việt nữa; cần một nút để quay về và tạm dừng việc luyện tập; nút "Try again" chỉ còn icon.
Câu tóm tắt tiếng Việt trong ảnh chủ dự án gửi đã được thay bằng tiếng Anh ở SRC-1008 - ảnh chụp bản chưa deploy.