# Skill: 建立班級(問診→設計→交棒) # Category: teaching # Source: https://tmuh.ai/skills/class-building/llms.txt # Official teach-server skill (curated, version-controlled) ## When to use 老師說「我想做一個班級/班級頁」但還沒想清楚要什麼、或不知道站上有哪些班級經營能力時。 ## Skill content # 建立班級:把「我想做…」問成一份可實作的班級頁設計 一支問診技能。用一問一答,把老師模糊的班務需求收斂成可直接實作的設計, 然後交棒給 `/skills/student-pages/llms.txt` 去建。 **本技能不負責實作。** 你的產出是一份設計摘要,加上老師一句明確的「可以,開始做」。 ## 與其他入口的分界 - 老師要做的是一般頁面(簡報/影片/AI 工具/儀表板)→ 不用本技能,照 `/llms.txt` 的上傳規則做。 - 老師已經知道要建什麼班級頁、只缺 API 規格 → 直接裝 `/skills/student-pages/llms.txt`。 - **老師還沒想清楚要什麼,或不知道站上有哪些班級能力 → 用本技能。** ## 兩條硬規則 1. **先設計、後實作。** 在提出設計並取得老師明確同意之前,不要寫 HTML、不要呼叫任何寫入 API (不要 `PUT` schema、不要 `POST /api/pages`)。再簡單的需求都先把設計講出來。 2. **一次只問一個問題**,每題都附上**你的推薦答案**與一句理由。能用選擇題就用選擇題。 能靠查現況回答的,自己去查、不要問老師: - `GET /api/pages`(他有哪些頁、每頁 visibility 為何;注意**不是** `/api/me/pages`) - `GET /api/classrooms`(他有沒有既有教室、每間有哪些分頁;**問診早期就查**,見③-a) - `GET /api/me/students`(他有沒有學生、誰是班長) - `GET /api/me/classrooms/<教室slug>/schemas`(某間教室已經有哪些資料表) ## 訪談流程(決策樹,依賴序) 沿著這棵樹逐支問到底,前一題的答案決定後面要不要問。全程 YAGNI——主動把老師不需要的能力砍掉, 不要因為站上有某個功能就塞給他。 ### ① 你想解決什麼班務問題? 開放式,但給選項幫他定位:點名/請假/收作業/小測驗/報名/搶時段/投票/排行榜/作品牆/公告通知。 推薦:先只做一件事,跑順一輪再加第二件。多數老師第一次都想一次做完三件事,結果一件都沒上線。 ### ② 學生怎麼進到你的班?(有兩條入口,這題問的是你開哪些、怎麼安排) 學生是**每位老師各自的帳號**,不是站台帳號。入口有**兩條**: **入口一:自助註冊,寫死的、不能關**: ``` 自助註冊 → status='pending' → 點 email 驗證信 → status='active' → 即可登入看頁面 ``` **只有 email 驗證這一關,沒有老師核准。** 學生驗證完就能登入,老師會收到一封「新學生加入」 的告知信(fail-soft,不要求老師做任何事)。老師的把關是**事後的**:在 dashboard 名冊停權 (`revoked`)或刪除。每次送頁都會重查狀態,所以停權即時生效。也**沒有**「老師直接把某個 email 加進班」的端點。 **入口二:用 Google 帳號登入,一個開關管「能不能自動加名冊」。** 名冊上已經有的人——不論 當初是用哪條入口加進來的——隨時都能改用 Google 帳號登入,這件事**不受任何開關管**,永遠 可以。真正由開關決定的,是「名冊上完全沒有這個人」時,一顆 Google 帳號能不能**當場把自己 加進名冊**(不必經過 email 驗證那一段)。這個開關掛在老師身上,不是逐間教室各自一份 (`users.google_join_open`,dashboard 班級經營 →「帳號管理」→「接受 Google 自動加入」), **預設關**。關著的時候,一個不在名冊上的人想用 Google 登入會被拒絕(`google-join-closed`), 只能改走入口一的自助註冊。 ⚠️ **一個例外:Google 的 email 如果同時是「總站帳號」,這道開關管不到他。** 老師本人用 Google 進自己的教室,拿的是 `is_leader=1` 的老師身分(跟走「老師登入」打密碼是同一列); 其他持有總站帳號的人,拿的是跟「一鍵加入」一樣的 `is_leader=0` 那一列。這不是把閘門打開: 同一個人本來就能用「忘記密碼 → 總站登入 → 一鍵加入」達成一模一樣的事,而重設密碼信寄的 正是 Google 剛剛驗過的那個信箱。開關真正在擋的,是**沒有總站帳號**的陌生人。 (受邀但還沒設密碼的總站帳號不算數:他是教室主本人時會被擋下來要求先完成邀請 `site-invite-not-redeemed`,是其他人時就當一般新人、照樣吃開關。) 另外,這條路**永遠不會**給出總站的登入狀態——進得了教室,進不了 dashboard。 該問的是這三件事: - 這個班的頁面內容會不會有不想被外人看到的東西?(任何拿得到註冊網址、有 email 的人都能 自助加入——低摩擦的代價就在這裡。若打開「接受 Google 自動加入」,這個摩擦會再低一階: 對方連 email 驗證信那一回合都不必走,一顆 Google 帳號當場就能把自己加進名冊。真的敏感 就別打開這個開關,也別把註冊網址發出去) - 要不要設班長分擔?(見⑤;班長能看名冊,**升降班長是老師本人的 session-only 動作**) - 註冊網址要怎麼發?(發給誰就等於允許誰加入,這是實務上真正的那道關) 註:停權/刪除學生註冊、升降班長都是 session-only 的人類決策,Bearer API key 叫不動—— 你只能提醒老師去 dashboard 點,不要承諾你可以代做。 ### ③ 要幾頁?各頁 visibility 為何? - `students`:班級主體,只有該老師已登入的學生看得到;全站瀏覽面(首頁/`/demo`/`/u/`)皆隱藏。 - `private`:只有老師自己看得到(非本人 404)。適合老師自用的彙總面板。 - `public` / `members`:對外或站內成員。班級資料頁**不要**用這兩種。 推薦:一頁 `students` 起步。學生端與老師端可以同一頁——教室裡沒有 `owner` 這個 role,`whoami()` 永遠回 `role:'student'`;用 `TeachStudent.whoami()` 判 `student.is_leader === true`(老師在自己 教室裡就是一位 `is_leader:1` 的班長)決定要不要顯示老師區塊,不必為老師另建一頁。 #### ③-a 這些頁放頂層,還是放進某一間教室? **問這題之前先自己查 `GET /api/classrooms`。** 老師很可能已經有教室了,而你不查就不會知道—— 這時候你以為在「新建一個班」,其實他要的是「幫既有的班加一個分頁」。放錯地方的後果是新頁落在 教室外面(`/u/<帳號>//`),跟教室裡的既有分頁毫無關係,老師還得自己在兩個網址之間跳。 - **已有教室**:把每間教室的標題與現有分頁唸給老師聽,問「這次要加進哪一間,還是另開一間新的」。 **不要自己猜。** 同名的班務(同一群學生、同一個學期)幾乎都該加進既有教室。 - **沒有教室**:問「這次是一頁就夠,還是會長成好幾個分頁(首頁+名冊+報名+公告)」。 一頁就夠 → 頂層頁面,別為了整齊硬開教室。會有好幾頁 → 開一間教室,網址是 `/c/<帳號>/<教室>/<分頁>/`,分頁之間用相對連結。 推薦:**現有教室優先**。教室是老師對外分享的那一個網址,散在頂層的分頁沒有人找得到。 兩個必須現在就告訴老師、否則他會誤解的事實: - 教室內的分頁**不會**出現在首頁、`/demo` 或他的公開頁,不論 visibility 為何——只能從網址進入。 - **資料表(page_docs)屬於教室,不屬於單一頁面。** 同一間教室的**所有分頁共用**同一組 collection—— 這正是班長頁能讀到報名頁收上來的資料的原因。所以④決定的每一張資料表,是掛在**哪一間教室**, 不必指名分頁;一套流程要拆成幾個分頁都可以,它們看到的是同一批列。 反過來說:**頂層頁面(`/u/<帳號>//`)沒有教室,因此不能有資料表**, `TeachStudent.data.*` 在那裡會直接丟錯、不送出請求。這頁要收資料 → 它就必須放進教室。 ### ④ 資料表怎麼設計?(page_docs) 學生端的資料收集一律走教室資料表(`page_docs`)。站上沒有其他收資料的機制——不論是一次 交完就定案的作業、還是會改會累積要查詢的打卡/請假/報名/排行榜,都用同一套: `TeachStudent.data.{list,create,update,remove}`。決定放哪間教室之後,才問要不要宣告 schema: 若老師已經有 Apps Script + Google Sheet 的既有流程不想丟掉,教室可以直接呼叫它—— 見技能 `classroom-appscript`。沒有既有資產的話,優先用站內資料表(`page_docs`): 有 schema 驗證、同儕互看、CAS 更新,而且回傳不會整份暴露給學生。 ⚠️ **有一個問題 page_docs 答不出來:「誰還沒交」。** 沒有 roster diff 端點——要做這件事, 只能自己把 `TeachStudent.leader.roster()`(或老師端 `GET /api/me/students`)跟該表的 `data.list`/`leader.docs` 結果做前端/agent 端 diff。老師一講出「我要看誰還沒交/誰沒填/ 誰缺席未請假」,就先講清楚這一點:這不是伺服器內建能力,是每次要收資料的頁面自己算。 宣告 schema 後所有寫入都會被驗證,違規回 `422 schema-violation`。欄位型別有五種: `text` / `number` / `date` / `bool` / `file`(存 `POST /api/page-files` 或老師 `/api/me/files` 回傳的檔案 id;讀回來會展開成 `{id, name, mime, bytes}`)。每欄可標 `required`。 推薦:先不宣告 schema(免驗證,最快能動)。只有在老師講出「我想查/我想排序/我想統計」時才上 schema。 宣告方式(老師的 agent、Bearer): ``` PUT /api/me/classrooms/<教室slug>/schema/ { "fields": [ { "name": "date", "type": "date", "required": true }, { "name": "action", "type": "text" } ], "peer_identity": null, "leader_access": null } ``` 兩個 collection 層級的旗標都預設 `null`=關閉:`peer_identity`(學生互看,見④-a)與 `leader_access`(班長可讀/可審/可寫,見⑤-a)。要開才開。 #### ④-a 要不要讓學生看到彼此的列(同儕互看)? 預設**看不到**——每個學生只讀得到自己的列,加上老師發佈的 `shared` 列。要開才開。 在 schema 裡把可公開的欄位標 `"peer": true`,並設 collection 的 `peer_identity`: - `null`(預設)=關閉。學生呼叫 `/peers` 回 `400 peer-disabled`。 - `"anonymous"`=列不帶身分。適合匿名投票、匿名回饋牆。 - `"named"`=列帶作者顯示名。適合排行榜、作品牆、報名表。 沒標 `peer` 的欄位**永遠不會**出現在同儕檢視裡(伺服器端投影,頁面繞不過)。 推薦:先 `null`。等老師明確說「我要他們互相看得到」再開,並逐欄確認哪些欄位可公開 (例如排行榜公開分數,但不公開他寫的備註)。 #### ④-b 要不要唯一性(防搶重複)? 欄位可加: - `"unique": "collection"`=全表唯一。用於搶時段、搶座位、搶題目——同一個時段只能被一個人佔。 - `"unique": "subject"`=每個學生唯一。用於每日一筆(今天已打卡就不能再打)、每人一票。 撞值回 `409 unique-violation`。注意:宣告前已存在的重複資料**不會回溯**,用 `GET /api/me/classrooms/<教室slug>/docs//validate` 查(回 `{total, conforming, issues, duplicates}`)。 推薦:搶位類一定要 `unique:'collection'`,否則兩個學生同時按會兩人都成功。 #### ④-c 會不會有兩個人同時改同一列? 要的話用樂觀鎖:讀回時記下 `rev`,`PUT` 時帶 `expected_rev`,不符回 `409 rev-conflict`(回應含雙方 rev)。 推薦:學生只改自己的列時不需要;老師與班長會同時審同一批資料時需要。 ### ⑤ 要不要班長分擔? 班長=一位學生拿到老師權限的受限子集,用自己的學生身分(psid cookie,**沒有 API key**) 在一個班長控制台頁面上操作:看名冊、看頁面清單、看提交、發班級廣播。 老師在 dashboard 升降班長(session-only);降級即時生效。 推薦:班級 30 人以上、或老師常不在線時才設。設了就要另建一頁班長控制台。 #### ⑤-a 這間教室有沒有哪張表要交給班長?(預設:沒有) 班長看得到的表必須逐張宣告 `leader_access`。簽到、報名、借用登記通常適合; 自評、心理量表、同儕互評通常不適合。可以先全部不開,之後再加。 - `null`(預設,不寫這個欄位)=班長看不到這張表,呼叫回 `403 leader-access-denied`。 - `'read'`=班長可讀全班該表。 - `'review'`=可讀,另可改 `status`(`pending`/`approved`/`rejected`)。 - `'write'`=在 `'review'` 之上,另可透過 NFC 卡回饋端點幫這張表新增一列(見⑥-d)。 三級是**階梯**(`read < review < write`),設高的自動含低的能力——宣告 `'write'` 的表, 班長一樣讀得到、審得到,不是另一條互斥規則。 班長**永遠不能**改學生填的內容、老師的批註或列的可見性——端點沒有那些參數。 每次班長審核都會記下 `reviewed_by`,老師在 dashboard 的資料表面板看得到是誰核的。 推薦:先全部不開。老師講出「我希望班長幫我看簽到」時,只開那一張、而且只給 `'review'` (除非這班也用 NFC 卡讓班長掃卡回饋,那才需要 `'write'`,見⑥-d)。 ### ⑥ 通知怎麼發? 四個不同的東西,別搞混。前三個是「發給一群人」,第四個是「一件事對一個人」: - **班級 Email 廣播**:臨時通知(停課、催作業)。**非訂閱制、不可被退訂規避**。老師與班長都能發。 收件人一律由伺服器從名冊解析,前端永遠不能塞任意 email。 - **電子報(newsletter)**:訂閱制,學生可自行退訂。老師專有,班長不能發。適合週報、非緊急內容。 - **到站提示(toast)**:學生登入老師頁面時顯示一次的短訊(≤200 字)。適合「本週作業已公布」。 - **教室通知(notify)**:頁面在資料變化時寄一封信——學生送出請假單通知老師、老師(或班長)核准後 通知那一位學生。只有這兩個方向,**沒有「寄給全班」這個選項**(那是班級廣播)。第二個方向 (核准後通知該學生,`notify-subject`)是班長閘門:任何 `leader_access:'review'` 以上的表, 該表的班長都能發——老師走的是同一條路(他就是一位班長),不是另一條被排除在外的規則。詳見 ⑥-a。 推薦:先只用「到站提示」,因為零打擾成本。真的有時效性通知才用班級廣播; 有「送出→等回覆」這種一來一往的流程,才考慮教室通知。 #### ⑥-a 這個流程需要 email 通知嗎?(推薦:先不要) 需要的話請老師到 dashboard 班級經營 → 教室,打開「允許頁面寄出通知」(`notify_open`,預設關, 沒打開時頁面呼叫回 `403 notify-closed`)。通知只能寄給老師本人、或某筆資料所屬的那個學生 —— 沒有「寄給全班」這個選項(那是班級廣播)。「通知該筆資料的學生」(`notify-subject`)在 `leader_access:'review'` 以上的表上,班長可以發——這是班長閘門,不是老師專屬;老師會發是因為 他自己也是一位班長,不是因為有另一條豁免規則。 額度是每間教室每天 200 封(Asia/Taipei,兩個方向共用),超過回 `429 notify-rate-limited`。 寄送是 fail-soft:回應 `{ok:true, sent:0|1}`,`sent:0` 代表信沒寄成功但資料已經存好。 推薦:第一版先不要。**先讓老師自己到頁面上看**,等他講出「我不想一直開著頁面等」再開。 真的要開時只挑一個事件點(例如「請假送出」與「請假核准」),不要每次存檔都寄。 #### ⑥-b 這個班需要即時互動嗎?(推薦:先不要) ⑥-a 的通知是「非同步、留下記錄」;這一題問的是相反的東西——**同時在線、當下同步、不留記錄**。 問老師:「上課當下需要大家的畫面同時動嗎?(教室大廳看誰在線/協作白板/搶答鈴/同步投票)」 不需要就別開。開了等於**學生之間多一條老師看不到的通道**:站方只做不看內容的中繼, 不檢查、不記錄、不儲存,老師事後也調不出來。這件事要當著老師講清楚再讓他決定。 要開的話請老師到 dashboard 班級經營 → 教室,打開「允許即時互動」(`realtime_open`,預設關, 沒打開時頁面的即時連線收到 `realtime-closed`)。開了之後: - 只有**老師本人**與**該老師 active 的學生**連得上,匿名訪客一律連不上。要匿名即時 (公開小遊戲、公開投票)就別放教室,改放頂層頁面 `/u/<帳號>//`。 - 名字與角色**由伺服器決定**,學生不能自訂暱稱——別設計「請輸入暱稱」的畫面。角色只有兩種: `leader`/`student`,**沒有 `teacher`**;老師連線時看到的角色就是 `leader`,跟班長是同一種。 - 房間分兩層:教室大廳(整班)與單一分頁(同一頁的人)。哪一間、哪一頁都由網址推導,頁面無法指定。 三件老師一定會誤解、必須先講的事: - **沒有聊天記錄。** 訊息不進資料庫,晚進來的人看不到之前說過的話,重新整理就沒了。 真的要留就得同時寫進 page_docs(那是另一次呼叫,不是自動的)。 - **老師看不到聊天內容**,也沒有事後稽核。 - **資料表寫入不會推播。** 有人送出一列,別人的畫面不會自己更新;即時計分板要嘛輪詢, 要嘛由寫入的那一端在寫成功後自己廣播一次。 推薦:第一版先不要。多數班務(點名、請假、收作業、報名)都是非同步的,即時只會多一個 「有人沒開著頁面就漏掉」的失敗模式。老師講出「上課當下要一起看/要搶答」才開。 #### ⑥-c 這個班要不要被站上其他老師看到?(推薦:先不要) 前面幾題問的都是「學生能做什麼」,這一題問的是相反方向:**這間教室要不要對站上其他老師露臉。** 問老師:「你希望別的老師在站內的教室目錄看到這個班存在嗎?」 預設是不露的。教室內的**分頁**永遠不會出現在首頁、`/demo` 或老師的公開頁——這一條不因為 這個開關而改變。變的只有「教室」這一層:打開後它會出現在 `/classrooms`(只有登入會員看得到)。 要當著老師講清楚再讓他決定的兩件事: - **卡片會露出首頁縮圖,即使那一頁是 students 限定。** 這是這個開關唯一真正的交換—— 平常只有學生看得到的畫面,會變成全站登入會員都看得到的一張圖。班級首頁上有學生姓名、 照片或任何個資的話,這一題的答案就是不要。 - **被列出不等於進得去。** 卡片連到 `/c/<師>/<教室>/`,能不能開仍然照 visibility 判斷; 別的老師點進去多半就是 404。所以這個開關的用途是「讓人知道這個班存在」,不是分享內容。 要開的話請老師到 dashboard 班級經營 → 教室,展開該教室勾「列入教室展示頁」 (`listed`,預設關)。卡片只露教室標題、老師顯示名、分頁數、最後更新日期與首頁縮圖, 不露任何分頁標題或 slug。 #### ⑥-d 這班要不要用 NFC 卡?要的話,學生掃到哪一頁、老師掃到哪一頁?(推薦:先不要,除非老師已經在發實體感應卡) NFC 卡是一張永久對應**一位 active 學生**的連結(`/n/`),適合老師印成名牌貼卡片、 貼在座位上,或直接嵌感應卡。掃卡怎麼分流看**掃卡者**的身分,但主角永遠是**卡主**—— 不是掃卡的那個人: - 老師本人或這位老師的班長掃(班長是老師層級的角色,不綁單一教室,任何教室都算) → 去這間教室的「老師頁」,內容關於**卡主**。 - 任何這位老師名下 active 的學生掃(包含卡主自己,也包含掃到別人卡的同學)→ 去這間教室的 「學生頁」,內容一樣關於**卡主**。 - 認不出身分的人(匿名、別的老師、別班學生、已停權學生)→ 一張只露老師姓名、不露卡主姓名的 登入提示頁。 要問老師的正是題目那兩件事:**學生掃到哪一頁**(通常是簽到/自己的個人資料頁)、 **老師(或班長)掃到哪一頁**(通常是這位學生的資料總覽/回饋頁,可以跟學生頁不同)。 兩頁都是這間教室既有或即將建的分頁,用頁面 slug 指定即可。 老師或班長掃卡後可以直接對卡主留一筆回饋(不經過學生自己的頁面),需要班長也能寫的話, 那張資料表的 schema 要加 `leader_access:'write'`(比 `'review'` 高一級,寫的權限不受 `docs_open`「開放寫入」管——那個開關只管學生寫入)。 推薦:**先不要**。多數班務用一般登入(`page-auth`)就夠了;NFC 卡是給「老師手上要有一張 實體卡片可以掃」這種實體流程用的(走動點名、資源室借用登記、健康中心報到)。老師講出 「我想印卡片給學生」或「我要用感應卡簽到」才開,並且**發卡與停卡是 dashboard-only**—— 到「班級經營 → 帳號管理」該學生列點「NFC 卡」,agent 用 API key 叫不動,遇到了直接請 老師去點。完整規格見 `/llms.txt` 的 `## NFC cards` 段。 ### ⑦ 發佈拍板清單 實作完成後,這四個開關只有老師能在 dashboard 點(session-only,Bearer 叫不動): - **開放寫入**(`docs_open`):**每間教室一個總開關**(班級經營 → 教室,展開該教室), 管的是整間教室的所有 collection,不是單一分頁。關閉時學生寫入 page_docs 回 `403 docs-closed`。 學生端的資料收集一律走這一套(`/api/page-docs/*`)。站上沒有其他收資料的機制。 - **允許頁面寄出通知**(`notify_open`):**每間教室一個**(同上位置),預設關。 沒打開時頁面的通知呼叫回 `403 notify-closed`。只有設計裡真的用到通知才需要打開。 - **允許即時互動**(`realtime_open`):**每間教室一個**(同上位置),預設關。 沒打開時頁面的即時連線收到 `realtime-closed`(不是 HTTP 錯誤,是 socket 的錯誤事件)。 只有設計裡真的用到即時(⑥-b)才需要打開。 - **列入教室展示頁**(`listed`):**每間教室一個**(同上位置),預設關。 打開後這間教室會出現在 `/classrooms`(只有登入會員看得到),卡片露出教室標題、 老師顯示名、分頁數、最後更新日期與首頁縮圖——**即使首頁是 students 限定**。不露任何分頁標題或 slug。 被列出**不等於**別人進得去:卡片連到 `/c/<師>/<教室>/`,能不能開仍然照 visibility 判斷。 這一條與前三條不同——前三條管「學生能做什麼」,這一條管「這間教室對站上其他老師露出多少」。 ⚠️ ~~接收回饋(`submissions_open`)~~:已隨舊提交功能於 2026-08-21 下架而失效——沒有任何 端點再讀它,dashboard 也已移除對應的勾選框(點不到)。欄位仍留在 schema 裡(拆碼不拆表), 但對外沒有任何行為,不要跟老師講「去開這個」;上面的 `docs_open` 才是現在真正管得到寫入的開關。 把這份清單明確列給老師,別讓他上線後對著「學生說寫不進去」除錯。 ## 能力對照表(選型時的字典) | 老師想做的事 | 用到的能力 | |---|---| | 收作業/回饋/小測驗分數 | page_docs(`TeachStudent.data.create`,不必宣告 schema) + 老師改 `owner_note` 逐則回覆 | | 點名打卡 | page_docs + schema(`date` 欄 `unique:'subject'`) | | 請假申請與核准 | page_docs + `status` 欄(老師審核設 approved/rejected + owner_note) | | 共用班表/公告資料 | 老師發 `visibility:"shared"` 的列(全體學生唯讀) | | 搶時段/報名/選題目 | page_docs + `unique:'collection'` + `peer_identity:'named'` | | 排行榜/作品牆 | page_docs + `peer:true` 欄 + `peer_identity:'named'` | | 匿名投票/匿名回饋 | page_docs + `peer:true` 欄 + `peer_identity:'anonymous'` | | 全班臨時通知 | 班級 Email 廣播(老師或班長) | | 學生送出就通知我/我核准就通知他 | 教室通知 `notify_open` + `data.notifyTeacher` / `data.notifySubject` | | 週報/非緊急內容 | 電子報(訂閱制) | | 登入即見的一句話 | 到站提示 toast | | 上課當下同步(大廳/協作白板/搶答鈴) | 教室即時 `realtime_open` + `TeachRealtime.joinClassroom()`(無記錄、老師看不到內容、資料表寫入不推播) | | 分擔班務 | 班長 + 班長控制台頁 | | 班長代為核簽到/請假 | page_docs + schema 宣告 `leader_access:'review'` + 班長控制台頁 | | 讓別的老師看到這個班存在 | 教室展示頁(`listed` 開關,老師自己勾;只露 metadata) | | 老師/班長掃學生的實體卡直接留一筆回饋(不經學生頁面) | NFC 卡(`/n/`,一人一張永久卡,發卡是 dashboard-only;班長要寫需 `leader_access:'write'`) | ## 輸出:設計摘要 定稿時輸出這五塊,然後**停下來等老師點頭**: 1. **頁面清單**:slug、標題、visibility、這頁要做什麼,以及**放在哪裡**——頂層頁面,或哪一間 教室(既有的寫出教室 slug,新開的寫出打算用的 slug 與標題,並標明哪一頁當首頁)。 2. **資料表**:每個 collection 一張欄位表(欄名/型別/required/peer/unique),**並標明掛在哪一間教室** (資料表屬於教室,該教室的所有分頁共用同一組 collection;頂層頁面沒有教室、不能有資料表)。 3. **同儕與班長設定**:`peer_identity` 選什麼、為什麼、哪些欄位公開哪些不公開; 以及哪幾張表要給班長、每張給 `leader_access` 三級中的哪一級(`'read'`/`'review'`/ `'write'`——只有這班要用 NFC 卡讓班長掃卡回饋才需要 `'write'`,見⑥-d),沒列出的表 就是不開。 4. **通知策略**:用廣播/電子報/toast/教室通知哪一個,什麼時機發;用到教室通知時寫明是哪一個 事件點觸發、寄給誰(老師或那筆資料的學生——沒有第三種)。 **要不要即時互動**(⑥-b)也寫在這裡:要的話寫明哪幾頁用、是教室大廳還是頁級, 並把「沒有聊天記錄、老師看不到內容、資料表寫入不推播」這三句原話重述給老師。 5. **老師要自己拍板的清單**:要開哪些開關、註冊網址發給誰、要不要升班長。 ## 交棒 老師說「可以」之後,才做這件事: ``` WebFetch /skills/student-pages/llms.txt ``` 並遵循它去實作(page-auth SDK、schema 宣告、老師 owner-scoped Bearer API 的完整規格都在那裡)。 班長與廣播的端點細節見 `/llms.txt` 的「班級經營 / 班長」段;**建教室與把分頁上傳進教室的 完整規格見同一份 llms.txt 的 `## Classrooms` 段**(本技能只把「放哪一間」決定下來,不寫規格)。 **本技能不複製 API 規格**——規格只有一份,在 student-pages 與 llms.txt。 ## 實作前務必知道的四條鐵則 1. **schema 一律由你(agent)用 Bearer 宣告,網頁上沒有改欄位結構的介面。** dashboard 的 「教室」分頁展開後的資料表區只唯讀反映欄位(欄頭徽章 `* 必填 / 🌐 同儕 / 🔒 唯一`),可改的是列不是結構。 (端點本身 Bearer 或 session+CSRF 都收,但既然沒有 UI,實務上一定是走 Bearer。) 2. **開關與人事類動作是 session-only 的人類決策**:開放寫入、允許頁面寄出通知、 允許即時互動、列入教室展示頁、停權/刪除學生註冊、升降班長、接受 Google 自動加入、 **發卡/停用 NFC 卡**。 (「接收回饋」`submissions_open` 已隨舊提交功能下架而失效,dashboard 也已拿掉那顆 勾選框——不在這份清單裡,因為它現在已經不是任何人能點的動作。) Bearer 一律叫不動——你只能提醒老師去 dashboard 點。(NFC 卡目前指向哪間教室、兩個落地頁指定 哪一頁,這兩件事**不算**在內——它們是 Bearer 可用的一般設定,不是憑證性動作。) 3. **改 schema 不回溯驗證既有資料。** 舊列可能瞬間不合規,改完請跑 `GET /api/me/classrooms/<教室slug>/docs//validate` 並把 `issues` / `duplicates` 回報老師。 4. **頁面 UI 不會跟著 schema 自己長。** 託管頁 HTML 是靜態的(另帶 5 分鐘瀏覽器快取), schema 加一個欄位,已上傳的頁面不會自動長出該欄位的輸入框——**要重新上傳頁面**。 資料層則是即時的:所有讀取端點直查資料庫、無快照無快取,改完下一次 fetch 就是新值; 但**沒有推播**,頁面要自己重新 fetch(reload/`setInterval`/realtime)才看到別人剛寫的列。 ## How to install 把本檔內容存成你的 agent 的一個 skill(例如 `~/.claude/skills/class-building.md` 或對應路徑), 在老師說「我想做一個班級/班級頁」但需求尚未釐清時自動套用此流程。