背景 — 運用対応で実際に誤認が起きた
先生から「クラスを誤ってアーカイブしたので復旧してほしい」という依頼を受けて調査したところ、
Admin では「復元済み」に見えるのに、実際には先生の画面から見えないままという状態を運用者が
判別できなかった。原因は Admin における クラス(学級)と課題(1 授業)の表現の混同。
ドメインモデルは 2 階層(docs/classroom/README.md):
| 概念 |
日本語 |
テーブル |
主キー |
| Group |
クラス(学級) |
ClassroomGroups |
groupId |
| Classroom |
課題(1 授業) |
Classrooms |
classroomId |
Classrooms テーブルが「課題」を保持しているというテーブル名自体のねじれが根本にあり、
それが Admin の API 名・UI 文言・タブ名にそのまま漏れている。
現状の問題(コードで確認済み)
1. Admin の「クラス」操作が、実際には課題を操作している
packages/admin/src/components/classrooms-view.jsx:247 — このクラスをアーカイブしますか?
- 同
:248 — このクラスを利用中に戻しますか?
- 同
:420 — このクラスを復元しますか?
- 同
:551 — 該当する削除済みクラスはありません。
- 同
:734 — タブ名 クラス検索
いずれも実際の書き込み先は infra/smalruby-admin/lambda/handler.ts の handleSetClassroomStatus
(PATCH /admin/classrooms/{classroomId})で、CLASSROOMS_TABLE(= 課題)にしか書かない。
クラス(ClassroomGroups)の状態は一切変わらない。
2. 課題一覧・詳細に「親クラスがアーカイブ中」が出ない
mapClassroomForAdmin(infra/smalruby-admin/lambda/handler.ts:501 付近)は groupId を返すだけで、
親クラスの status を返さない。そのため:
- 課題が
active なら Admin は 🟢「利用中」と表示する
- しかし親クラスが
archived だと、先生の画面にはその課題は表示されない
- 運用者は「生きている=先生に見えている」と誤読する ← 今回の誤認の直接原因
3. 誤解を強める文言がある
packages/admin/src/components/classrooms-view.jsx:376
このクラスはまだ存在しています。アーカイブからの復旧は先生自身のクラス管理画面から行えます。
「クラス」と言いつつ判定しているのは課題の生存。親クラスがアーカイブ中のケースにこの文が出ると、
運用者は「先生側で復旧できる状態にある」と読むが、案内すべき操作(クラス一覧 →
「アーカイブ済みのクラス」→「元に戻す」)とは別物になる。
4. クラス(学級)単位の検索・アーカイブ解除が Admin に無い
Admin Lambda が GROUPS_TABLE に触るのは handler.ts:906-912 の 1 箇所だけで、しかも
読み取り(TTL 失効データのスナップショット復元時に、親クラスが「完全に消えているか」の確認)。
liveGroup.Item が返る=生きているが archived のケースは何もしない。
結果、ClassroomGroups.status を active に戻せるのは先生用 UI の
PATCH /classroom-groups/{groupId}(infra/smalruby-classroom/lambda/handler.ts:2474 付近)だけで、
Admin も運用 CLI(bin/restore-classroom.ts = TTL 失効専用)もクラスのアーカイブ解除を持たない。
先生が UI 操作に到達できない問い合わせに運用者が対応できない。
やること
A. 用語の徹底監査と統一(最優先・他の作業の前提)
- 用語辞典を作る(
docs/classroom/README.md または docs/admin/README.md に節を追加):
クラス(学級)= Group / groupId、課題(1 授業)= Classroom / classroomId。
「組」「学級」「クラスルーム」などの揺れをどう扱うかも決める。
- Admin の全文言を監査して、対象が課題なら「課題」、クラスなら「クラス」と正しく呼ぶ。
packages/admin/src/** のユーザー可視文字列を全て洗い出して確認する(上記 5 箇所は既知だが、
それだけで終わりにせず網羅する — .claude/rules/ の「パターンは全インスタンスを確認」に従う)。
- 識別子・型・関数名・data-testid・API パスについても、課題を指しているのに
class と
名乗っているものを洗い出す。互換性の都合で API パス(/admin/classrooms)を変えられない場合は、
変えない理由と対応表をコードコメント + docs に残す(勝手に破壊的変更をしない)。
- teacher UI 側(
packages/scratch-gui/src/components/classroom-modal/**)にも同種の揺れが無いか
確認する。あれば本 EPIC の子 Issue として切り出す。
B. 課題に親クラスの状態を出す
- Admin API の課題レスポンスに親クラス情報(
groupId / クラス名 / groupStatus)を含める。
- 課題一覧・詳細に「親クラスがアーカイブ中 — 先生には表示されません」という警告バッジ/
注意文を出す。課題単体が active でも実効的に見えないことが一目で分かること。
restore-admin-alive の文言を、課題が生きている場合と親クラスがアーカイブ中の場合で
出し分け、後者では先生に案内すべき正確な操作手順(クラス一覧 → アーカイブ済みのクラス →
元に戻す)を提示する。
C. Admin にクラス(学級)の検索とアーカイブ解除を追加
- Admin API に クラス(
ClassroomGroups)の検索・詳細・status 更新を追加する
(命名は A の用語辞典に従う。既存の課題向けエンドポイントと明確に区別できるパスにする)。
- Admin SPA に「クラス」タブ(または既存タブ内の明確な切り替え)を追加し、
アーカイブ⇄利用中の切り替えと、そのクラスに属する課題の一覧を表示する。
- 権限・監査ログ(
audit(...))は既存の課題操作と同じ扱いにする。
- 破壊的操作なので確認ステップを踏む(既存の 2 段階確認パターンに合わせる)。
設計上の注意
- 単一ライターを壊さない: クラスの状態を書く経路が「先生 UI」と「Admin」の 2 つになる。
どちらも同じ更新形(status + updatedAt)にし、restoredAt 等のスタンプ規約を揃える。
- TTL を触るときは実行時点から再設定(過去の TTL のまま書くと即再削除される。
docs/classroom/operations.md の既存規約)。
- クラスをアーカイブ解除しても、中の課題の
status は変えない(今回のケースは課題は最初から
active。無関係な課題を勝手に復活させない)。
- 同名クラスが並ぶケースが実際に起きている(Google Classroom 連携での二重作成)。一覧では
クラス名だけで判断できないため、年度・人数・中の課題名・作成日時を併記して区別できること。
DoD
背景 — 運用対応で実際に誤認が起きた
先生から「クラスを誤ってアーカイブしたので復旧してほしい」という依頼を受けて調査したところ、
Admin では「復元済み」に見えるのに、実際には先生の画面から見えないままという状態を運用者が
判別できなかった。原因は Admin における クラス(学級)と課題(1 授業)の表現の混同。
ドメインモデルは 2 階層(
docs/classroom/README.md):ClassroomGroupsgroupIdClassroomsclassroomIdClassroomsテーブルが「課題」を保持しているというテーブル名自体のねじれが根本にあり、それが Admin の API 名・UI 文言・タブ名にそのまま漏れている。
現状の問題(コードで確認済み)
1. Admin の「クラス」操作が、実際には課題を操作している
packages/admin/src/components/classrooms-view.jsx:247—このクラスをアーカイブしますか?:248—このクラスを利用中に戻しますか?:420—このクラスを復元しますか?:551—該当する削除済みクラスはありません。:734— タブ名クラス検索いずれも実際の書き込み先は
infra/smalruby-admin/lambda/handler.tsのhandleSetClassroomStatus(
PATCH /admin/classrooms/{classroomId})で、CLASSROOMS_TABLE(= 課題)にしか書かない。クラス(
ClassroomGroups)の状態は一切変わらない。2. 課題一覧・詳細に「親クラスがアーカイブ中」が出ない
mapClassroomForAdmin(infra/smalruby-admin/lambda/handler.ts:501付近)はgroupIdを返すだけで、親クラスの
statusを返さない。そのため:activeなら Admin は 🟢「利用中」と表示するarchivedだと、先生の画面にはその課題は表示されない3. 誤解を強める文言がある
packages/admin/src/components/classrooms-view.jsx:376「クラス」と言いつつ判定しているのは課題の生存。親クラスがアーカイブ中のケースにこの文が出ると、
運用者は「先生側で復旧できる状態にある」と読むが、案内すべき操作(クラス一覧 →
「アーカイブ済みのクラス」→「元に戻す」)とは別物になる。
4. クラス(学級)単位の検索・アーカイブ解除が Admin に無い
Admin Lambda が
GROUPS_TABLEに触るのはhandler.ts:906-912の 1 箇所だけで、しかも読み取り(TTL 失効データのスナップショット復元時に、親クラスが「完全に消えているか」の確認)。
liveGroup.Itemが返る=生きているがarchivedのケースは何もしない。結果、
ClassroomGroups.statusをactiveに戻せるのは先生用 UI のPATCH /classroom-groups/{groupId}(infra/smalruby-classroom/lambda/handler.ts:2474付近)だけで、Admin も運用 CLI(
bin/restore-classroom.ts= TTL 失効専用)もクラスのアーカイブ解除を持たない。先生が UI 操作に到達できない問い合わせに運用者が対応できない。
やること
A. 用語の徹底監査と統一(最優先・他の作業の前提)
docs/classroom/README.mdまたはdocs/admin/README.mdに節を追加):クラス(学級)= Group /
groupId、課題(1 授業)= Classroom /classroomId。「組」「学級」「クラスルーム」などの揺れをどう扱うかも決める。
packages/admin/src/**のユーザー可視文字列を全て洗い出して確認する(上記 5 箇所は既知だが、それだけで終わりにせず網羅する —
.claude/rules/の「パターンは全インスタンスを確認」に従う)。classと名乗っているものを洗い出す。互換性の都合で API パス(
/admin/classrooms)を変えられない場合は、変えない理由と対応表をコードコメント + docs に残す(勝手に破壊的変更をしない)。
packages/scratch-gui/src/components/classroom-modal/**)にも同種の揺れが無いか確認する。あれば本 EPIC の子 Issue として切り出す。
B. 課題に親クラスの状態を出す
groupId/ クラス名 /groupStatus)を含める。注意文を出す。課題単体が
activeでも実効的に見えないことが一目で分かること。restore-admin-aliveの文言を、課題が生きている場合と親クラスがアーカイブ中の場合で出し分け、後者では先生に案内すべき正確な操作手順(クラス一覧 → アーカイブ済みのクラス →
元に戻す)を提示する。
C. Admin にクラス(学級)の検索とアーカイブ解除を追加
ClassroomGroups)の検索・詳細・status更新を追加する(命名は A の用語辞典に従う。既存の課題向けエンドポイントと明確に区別できるパスにする)。
アーカイブ⇄利用中の切り替えと、そのクラスに属する課題の一覧を表示する。
audit(...))は既存の課題操作と同じ扱いにする。設計上の注意
どちらも同じ更新形(
status+updatedAt)にし、restoredAt等のスタンプ規約を揃える。docs/classroom/operations.mdの既存規約)。statusは変えない(今回のケースは課題は最初からactive。無関係な課題を勝手に復活させない)。クラス名だけで判断できないため、年度・人数・中の課題名・作成日時を併記して区別できること。
DoD
packages/admin/src/**のユーザー可視文言を全数監査し、課題を「クラス」と呼ぶ箇所が 0 件restore-admin-aliveの文言が、課題生存/親クラスアーカイブ中で正しく出し分けられているdocs/admin/README.md(および必要ならdocs/classroom/*)を更新docs/admin/screenshots/を更新(docs/_screenshot-guidelines.mdに従う)Admin 上で「先生には見えていない」と判別できること)