Imported from trannguyenthanhthao2024-art/TeachingOS (
tier 1/AGENTS.md). Install upstream withnpx skills add trannguyenthanhthao2024-art/TeachingOS --skill tier 1. Copyright stays with the author.
AGENTS.md — Hướng dẫn vận hành cho AI Coding Agent
Đây là file đầu tiên agent phải đọc. Nó quy định cách làm việc trong repo này: đọc gì, xây gì trước, luật bất di bất dịch, quy ước code, cách chạy/test, và khi nào phải dừng lại hỏi người. Khi có mâu thuẫn giữa các tài liệu, AGENTS.md thắng về quy trình; các file SPEC thắng về chi tiết sản phẩm.
1. Đọc theo thứ tự này trước khi code
AGENTS.md(file này) — luật & quy trình.SPEC-Teaching-OS.md— kiến trúc, mô hình dữ liệu, lộ trình, tech stack.SPEC-Teaching-OS-Screens.md— danh mục màn hình & thành phần UI.SPEC-Teaching-OS-Realtime-Flow.md— trình tự sự kiện realtime (bắt buộc đọc kỹ trước khi làm Session Engine).
Các file sẽ bổ sung sau (chưa có thì cứ theo SPEC):
data-model-types,plugin-authoring-guide,ai-generation-spec,backend-api-spec,database-rls-spec,task-backlog.
Nếu một yêu cầu không nằm trong các tài liệu trên → xem mục §12 (Khi nào dừng lại hỏi). Không tự bịa tính năng.
2. Sản phẩm là gì (một đoạn)
Teaching OS là web app giúp giáo viên thiết kế một lần một tiến trình bài học tương tác (Scene chứa Widget), khi lên lớp thì mọi thiết bị học sinh đồng bộ realtime theo điều khiển của giáo viên, và mọi tương tác (poll/quiz/padlet/game) được thống kê ngay tại chỗ theo cá nhân/nhóm. Không phải phần mềm trình chiếu — định vị là "hệ điều hành cho tiết dạy".
3. LUẬT BẤT DI BẤT DỊCH (không được vi phạm ở bất kỳ đâu)
Vi phạm các luật này = làm lại. Đây là cốt lõi của sản phẩm.
- AI chỉ sinh DỮ LIỆU JSON đúng schema. Không bao giờ sinh HTML/JSX/CSS. Toàn bộ hiển thị do Renderer (React) đảm nhiệm.
- Mọi widget render qua đúng một
WidgetRendererđọctype→ dispatch tới plugin. Cấm hard-code từng màn hình. - Tách
Lesson(bản thiết kế tĩnh) khỏiSession(buổi dạy trực tiếp). Không trộn hai khái niệm. - Server (Postgres) là nguồn sự thật cho trạng thái buổi dạy (
current_scene_index,interaction_locked). Client học sinh không tự quyết scene. - Không đẩy trước scene chưa tới cho học sinh. Chỉ đồng bộ scene hiện tại.
- Server luôn kiểm tra quyền ở mọi
participant.submit(status, khóa tương tác, widget thuộc scene hiện tại, rate-limit, idempotency). Ẩn nút ở client chỉ là UX — không tin client. - Thêm widget mới = thêm một plugin (schema + renderer + editor + aiPrompt), KHÔNG sửa lõi.
- Schema-first bằng Zod. Data đi vào hệ thống (từ GV nhập hoặc AI sinh) đều phải validate Zod trước khi dùng.
- Mọi lời gọi LLM đi qua backend/edge function. Cấm để API key ở client.
- Bám lộ trình. MVP trước. Không tự nhảy sang marketplace / knowledge graph / 40 widget / AI video.
4. Phạm vi HIỆN TẠI — chỉ làm đúng phần này (Phase 1 / MVP)
LÀM:
- Auth GV (Supabase) + HS vào lớp bằng mã phòng, không cần tài khoản.
- Lesson Builder cơ bản (tạo/sắp Scene, thêm/sửa Widget).
- 7 widget MVP:
text,image,video,poll,quiz,open-question,timer. - Session Engine realtime: mở phòng → join → start → next/prev đồng bộ → khóa/mở → submit → thống kê realtime → end.
- Chia nhóm + thống kê theo nhóm.
- AI sinh 1 tầng: prompt → Lesson JSON hợp lệ → nạp Builder.
KHÔNG LÀM lúc này (để P2/P3 — nếu thấy cần, xem §12):
- Marketplace, Question Bank, Plugin SDK bên thứ ba.
- Knowledge Graph, pipeline AI nhiều tầng, Teacher Script.
- Nhập giáo án Word/PDF (đó là P2).
- Game engine, whiteboard, mindmap, AI ảnh/giọng/video.
- Offline/PWA.
- Tối ưu sớm (vd tách bảng scenes/widgets — MVP để trong 1 cột jsonb).
Chuẩn "xong MVP": xem §9 và Definition of Done trong SPEC-Teaching-OS.md §13.
5. Tech stack (đã chốt — không đề xuất thay thế)
| Lớp | Dùng |
|---|---|
| Frontend | React + Vite + TypeScript |
| UI | Tailwind CSS + Radix/shadcn |
| Animation | Framer Motion |
| State client | Zustand |
| Kéo-thả | dnd-kit |
| Rich text | TipTap |
| Realtime + Auth + DB + Storage | Supabase (Postgres + Realtime Broadcast/Presence + Auth + Storage) |
| Validate | Zod |
| LLM | gọi qua edge function (nhà cung cấp cấu hình được) |
Realtime nâng cao (Node + Socket.IO) chỉ cân nhắc ở P2, không dựng ở MVP.
6. Cấu trúc thư mục (theo SPEC §10)
src/
├── app/ routing, layout, providers
├── auth/ đăng nhập GV, join HS
├── builder/ Lesson Builder (S4) + AI panel (S5)
├── player/ preview (S6), live host (S8), student view (S11)
├── session/ session engine client: store, realtime hooks, event handlers
├── plugins/ MỖI widget một thư mục con
│ ├── registry.ts
│ └── poll/ (schema.ts, Renderer.tsx, Editor.tsx, prompt.ts, aggregate.ts)
├── ai/ prompt builder, LLM client, validator (Zod)
├── lib/ supabase client, utils
└── types/ Lesson, Scene, Widget, Session, Response...
supabase/
└── migrations/ SQL + RLS
Đặt đúng file vào đúng thư mục. Không tạo thư mục ngoài sơ đồ trừ khi có lý do rõ và ghi vào PR.
7. Quy ước code
- TypeScript strict. Không dùng
any; nếu bí, dùngunknown+ thu hẹp kiểu. - Zod là nguồn chân lý về kiểu dữ liệu domain. Suy ra type bằng
z.infer<>; đừng khai báo type domain tách rời khỏi schema. - Component: hàm + hook. Một component một việc. Tách logic realtime ra hook (
useSession,useParticipant), không nhét vào JSX. - State: dùng Zustand cho state phiên/builder; không lạm dụng context lồng nhau.
- Đặt tên: component
PascalCase; hookuseCamelCase; file plugin theotype(thư mụcpoll/). - Không hard-code chuỗi realtime rải rác: gom tên sự kiện & kênh vào một chỗ (
session/events.ts) — khớp event contract SPEC §5.2. - Không side-effect trong render. Gọi API/subscribe trong
useEffect/hook. - Ưu tiên đọc lại tài liệu hơn là đoán. Nếu payload/sự kiện có trong Realtime-Flow, dùng đúng tên & cấu trúc ở đó.
- i18n-ready: chuỗi hiển thị tiếng Việt; không chèn text cứng lẫn trong logic (gom lại để sau dịch được). Với MVP có thể để tiếng Việt trực tiếp nhưng gom tập trung.
- Seam đa nền tảng (SPEC §3C): giữ logic realtime/submit trong hook thuần (
useSession,useParticipant,session/api.ts) + Zustand store thuần, tách khỏi JSX; không giả định "một host một kết nối" ở chỗ đếm/Presence/cảnh báo; truy cập nền tảng (camera/QR, localStorage, thông báo) qua lớp mỏng tronglib/, không rải rác; lõi không phụ thuộcwindow/DOM trực tiếp. → sau này lên native (Capacitor/React Native) và GV đa thiết bị không phải viết lại.
8. Quy ước realtime (đọc kèm Realtime-Flow)
- Một buổi dạy = một kênh
session:{sessionId}(Broadcast + Presence). - Control-state ghi DB trước, rồi broadcast
state.sync. Client áp dụng theots, bỏ qua message cũ. - Client (HS & host) luôn xin
state.synckhi join và khi reconnect bằng cách đọc DB — không giả định. participant.submitgắnclientMsgId(idempotency). Widget "một lần" ràng buộc unique(participant_id, widget_id).- Host tính aggregate từ Postgres Changes (INSERT
responses), cập nhật tăng dần + debounce ~200ms. - Lưu
participantIdvào localStorage để reconnect không tạo trùng. - Mục tiêu: đồng bộ scene < 1 giây với 40 HS.
9. Cách chạy, build, test (agent tự thiết lập & duy trì)
Khi khởi tạo dự án, tạo sẵn các script và ghi lại lệnh thật vào đây:
# cài đặt
npm install
# chạy dev (http://localhost:5173)
npm run dev
# kiểm kiểu + lint
npm run typecheck
npm run lint
# format
npm run format
# test (vitest, jsdom + @testing-library/react)
npm run test # chạy 1 lần
npm run test:watch # watch mode
# build
npm run build
# Supabase (local)
supabase start
supabase db reset # chạy migrations trong supabase/migrations
# Supabase project thật — deploy qua API bundling (không cần Docker):
# npx supabase@latest functions deploy <name> --project-ref <ref> --use-api
# cần SUPABASE_ACCESS_TOKEN (personal access token, supabase.com/dashboard/account/tokens)
supabase functions deploy generate-lesson --use-api
supabase functions deploy generate-widget --use-api
# Provider LLM = Google Gemini Flash (đổi từ Claude theo yêu cầu user — xem ROADMAP.md).
# Đổi provider chỉ cần sửa supabase/functions/_shared/llmClient.ts.
supabase secrets set LLM_API_KEY=<gemini-api-key> # LLM_MODEL mặc định gemini-flash-latest nếu bỏ trống
# Kiểm thử tải (T9.1) — chạy nhắm vào session THẬT đã live, trước khi dạy lớp đông
node scripts/load-test.mjs --url https://xxx.supabase.co --anon-key sb_publishable_xxx \
--join-code 4F7K2P --participants 40 --widget-id w-poll-1
Đã dựng (T0.1, xem ROADMAP.md): Vite 8 + React 19 + TS 6 (strict, path alias @/* → src/*) + Tailwind v4 (@tailwindcss/vite, token trong src/index.css theo design-system.md) + ESLint 10 (flat config eslint.config.js) + Prettier + Vitest 4 + Testing Library. Router: react-router-dom (src/app/router.tsx), route khung cho 7 màn hình MVP (đa số còn là placeholder, xem ROADMAP.md để biết milestone nào lấp nội dung).
Env (không commit giá trị thật): tạo .env.example với: VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY, và (phía server) LLM_API_KEY. Key LLM chỉ ở edge function, không có tiền tố VITE_.
Trước khi báo "xong" một task: typecheck + lint + test phải xanh, build chạy được.
10. Định nghĩa "hoàn thành" cho mỗi task
Một task chỉ xong khi:
- Đúng phạm vi §4 (không kèm tính năng ngoài yêu cầu).
- Không vi phạm luật §3.
-
typecheck,lint,test,buildđều pass. - Có test cho logic quan trọng (validate Zod, aggregate, kiểm quyền submit).
- Realtime (nếu có): đã thử reconnect + vào trễ + submit khi khóa (theo ca §10 của Realtime-Flow).
- Cập nhật tài liệu nếu thay đổi cấu trúc (lệnh chạy, env, thư mục).
11. Git & commit
- Nhánh theo task:
feat/session-engine,feat/poll-plugin,fix/reconnect. - Commit nhỏ, thông điệp rõ (Conventional Commits:
feat:,fix:,refactor:,test:,docs:). - Mỗi PR mô tả: làm gì, thuộc task nào, đã test ca nào, có đụng luật §3 không.
- Không commit secret,
.env, hay dữ liệu thật của HS.
12. Khi nào DỪNG LẠI và hỏi người (không tự quyết)
Dừng và hỏi khi:
- Yêu cầu mâu thuẫn với luật §3 hoặc nằm ngoài phạm vi §4 mà không rõ thuộc phase nào.
- Cần thêm phụ thuộc/thư viện ngoài stack §5.
- Một quyết định ảnh hưởng schema dữ liệu hoặc bảo mật (RLS, quyền submit).
- Tài liệu thiếu thông tin để làm đúng (vd chưa có schema của một widget).
- Có nhiều cách hợp lý và lựa chọn ảnh hưởng kiến trúc lâu dài.
Việc nhỏ, rõ, đúng phạm vi → cứ làm, không cần hỏi. Đừng hỏi những thứ đã có trong tài liệu — hãy đọc lại trước.
13. Bảo mật (must-do, không phải tùy chọn)
- RLS bật cho mọi bảng. GV chỉ chạm Lesson của mình; HS chỉ insert response của mình khi session
livevà chưa khóa. - Kiểm quyền
submitở server, không chỉ client. - Mã phòng đủ ngẫu nhiên; rate-limit join & submit.
- Không log dữ liệu định danh HS ra ngoài cần thiết.
- Key LLM/nhà cung cấp chỉ ở edge function.
14. Checklist khởi động (agent làm ngay khi bắt đầu Phase 1)
Theo Phụ lục A của SPEC-Teaching-OS.md:
- Khởi tạo Vite + TS + Tailwind + Supabase client + script §9.
-
types/+ Zod schema: Lesson, Scene, Widget, Session, Response. - Plugin registry + 3 widget tĩnh (text, image, video).
- Lesson Builder cơ bản (S4).
- 3 widget tương tác (poll, quiz, open-question) + aggregate.
- Session engine: tạo session, join code, join HS,
state.sync. - Next/Prev đồng bộ + khóa tương tác.
- Submit response + thống kê realtime cho GV.
- Chia nhóm + thống kê theo nhóm.
- AI 1 tầng (edge function) → validate → nạp Builder.
- Đạt Definition of Done (SPEC §13).
Làm tuần tự, mỗi bước có test, không gộp nhảy cóc. Báo tiến độ theo từng mục.