DEVELOPER DOCS

v1.1 路線圖(Fable 5 定案,2026-07-13)

原始檔 docs/roadmap.md · 在 GitHub 上檢視

v1.0.0 已於 2026-07-12 發行。本文件把 tasks.md 末尾的 Backlog(31 項)收斂成可執行的 v1.1 範圍, 並明確劃出「v1.2+ 再說」與「刻意不做」的界線。每項都回頭讀過實際程式碼確認現況; 與 Backlog 敘述不符者已註明。後續任何人(或模型)接手,以本文件為邊界。

v1.1 的收錄標準:真實學校在一個學期內會實際踩到,或開源後會傷到採用信任。 目標時程:2026 年 8 月排課季前發布——ops 佇列與部分排課兩項正是排課季的痛點。


已在 2026-07-13 出貨(不列入 v1.1)

  • E2E 迴歸套件納入 CI(原 Backlog「M0-3 E2E job」):CI 起全棧 + seed + Playwright 30 tests, E2E 紅燈不發映像。同時涵蓋了「compose 煙霧測試」一項。
  • npm ci + lock 檔:Backlog 說「未提交 package-lock.json」——已過時,lock 檔早已入庫, CI 已改用 npm ci

v1.1 範圍(依實作順序)

1. E2E 硬編日期到期修復 —— S,有死線:2026-11 前

  • 問題:多支 e2e spec 硬編未來日期(2026-11-11、學期 2026-09..2027-01);真實日期越過後 clock.is_past_slot 會拒絕代課指派,CI 將無聲轉紅
  • 影響誰:所有貢獻者——CI 一紅,其他迴歸都看不見了。
  • 做法:以「今天起算的下一個週三」動態產生請假日,學期起訖跟著今天推算。
  • 不做的後果:2026-11-11 起 CI 全紅,E2E 防護網等於拆掉。

2. 背景任務佇列拆分(default / ops)—— M/L,v1.1 旗艦項

  • 問題(M5 複審 A 正解):單一 RQ worker 循序執行;60 班自動排課跑數分鐘期間, 組長按「匯出課表」「立即備份」會排隊到逾時失敗。資料安全洞已用 cancel-on-timeout + 還原前 409 封死,但體驗洞還在——排課季正是匯出最頻繁的時候。
  • 影響誰:排課季的教學組長,幾乎必踩。
  • 做法方向:ops 佇列(匯出/備份/還原)+ 第二個 worker 容器(同一 worker 映像、 command: worker --queue ops)。排課永遠只走 default,故 ops worker 不會載入 ortools 的 求解路徑,記憶體預算約 +512MB(docker-compose.limits.yml 同步調整)。 需更新:compose(5→6 容器)、solver_busy() 的佇列範圍、部署/升級文件、備份還原的 409 判定。
  • 先做的理由:牽動容器架構與記憶體預算,後面所有項目的文件與 E2E 都疊在定案後的架構上。
  • 不做的後果:每個排課季都有組長回報「匯出壞掉」,對信任的傷害最大。

3. 部分排課三合一(排不下的課不再炸整鍋)—— M

三項同一子系統,一次做完:

  • 3a. 候選為空先 raise、輪不到 drop:model_builder 對「完全找不到可排時段」的課直接 SolverInputError → 整個部分排課失敗。但部分排課的承諾就是「排不下的列清單、其他照排」。 改為:建模前把候選為空的課移入未排清單,其餘正常建模。 不做的後果:資料越難排的學校,越用不了為他們設計的功能。
  • 3b. 未排清單持久化:目前只活在 Redis(24h TTL,progress.py);部分排課草稿可被 force 發布,之後沒有任何紀錄說哪些課沒排。改為隨草稿版本存 DB(新表或版本 JSON 欄), 報表與日後查詢都需要它。
  • 3c. 群組未排數灌水:_unscheduled() 按 assignment 逐筆記,跑班群組掉一格會被記 N 筆。 按排課單位(group)去重。
  • 驗收:含一個「有課完全排不下」的 e2e / 整合測試,確認列清單而非失敗。

4. 開新學期複製補全 —— S(兩項一起)

  • semester_copy.py 確認不帶學期起訖日、不帶 constraint_config(軟約束權重回預設)。 複製對話框加起訖日欄位、constraint_config 隨複製帶過去(或加勾選)。
  • 影響誰:每學期交替時的每一位組長(一年至少兩次,必踩)。
  • 不做的後果:新學期忘了補起訖日 → 請假/代課的「今日」判定全錯,查因很痛。

5. 小型加固批次 —— 全部 S,湊一批出貨

項目 現況(已驗證) 做法
班級名稱唯一性 ClassUnit 無 uq,可建兩個「301」 uq(semester_id, name) + 遷移前清重複 + API 409
/api/docs 正式環境公開 main.py 無條件開 預設關閉,.env 顯式打開(開發 compose 帶開)
主色對比未達 AA Naive 預設綠 #18a058 白字 ≈3.4:1 主色調深至 ≥4.5:1(如 #0d7a43 一帶),a11y 測試門檻同步提到 4.5
衝突定位不理會取消 conflict_explainershould_stop 把 control 傳進逐步試解迴圈,按取消得到 cancelled
check_feasibility 吞錯誤訊息 except SolverInputError: return "infeasible"(model_builder:968) 訊息記 log + 附進 conflict detail,建模 bug 不再偽裝成「資料無解」
清單查詢保護性上限 audit-logs 有 limit(≤500);substitution-log/leaves 無上限 先加伺服器端上限(如 1000)防整年資料整包拉;完整分頁 UI 留 v1.2

v1.2+(明確延後,不是忘記)

  • 完整清單分頁(UI + offset):單校一學期的量還撐得住,先靠上限保護;整年資料變慢時再做。
  • restore-upload 串流落地:await file.read() 整包進記憶體;現有 Caddy 200MB 上限 + 「超大 DB 走 volume 複製法」文件已緩解。
  • 還原溯源 restore.log + stale 警告持久徽章(M5 複審 G):presafe 檔名時戳暫可佐證。
  • CORS 文件化:Backlog 說「不可由 .env 設定」——不精確:cors_origins 是 pydantic-settings 的 list 欄位,本來就能用 env(JSON 格式)覆寫,只是沒文件、沒驗證。 v1.2 補文件與格式驗證即可,同源部署下本項無實害。
  • 前端 bundle 瘦身(1.4MB,app.use(naive) 全量註冊):校內網冷載 ~1 秒可接受; 改按需匯入時記得 e2e 全跑一次。
  • api 映像拆掉 ortools(660MB→約 200MB;已確認 ortools 在主依賴、api 容器用不到): 搬到 [export] 之類的 extra 由 worker 安裝;對頻寬敏感的部署才有感。
  • 求解前 hard-only 探測 + warm start;部分排課獨立短時限:解品質/速度優化,獨立可做。
  • 配課 API 直接擋群組節數不一致(409):pre-flight 已擋(group_shape_mismatch), API 層再擋只是把錯誤提早到建立當下。
  • 工程衛生:test_purity 補相對匯入掃描;slots_overlap/course_key 補邊界單元測試; 評估後端相依鎖定——pyproject 全為 >= 無上限,映像重建即靜默升級主版號 (2026-07-13 CI 首跑實例:redis-py 8 讓 RQ 阻塞取結果失效,匯出 500; 本機因 pip layer 快取測不到)。至少對 redis/rq/sqlalchemy/fastapi 加上限或引入 constraints 檔。
  • 科目匯入加 is_major:表單已可勾,匯入欄位是便利性。

v2(需要外部條件)

  • LINE 通知 adapter:走 LINE OA Messaging API,各校自申請 channel token + 綁定碼流程。 等有試用學校提出需求再做——沒有真實 OA 可測之前,寫了也驗不了。
  • 軟約束權重 UI:GET/PUT /api/solver/config 已在,滑桿是給進階使用者的; 等有學校真的想調權重再開工。

刻意不做(除非出現真實個案)

  • 「一門課逐節換教室」:現行 y[配課, 教室](一門課整學期一間教室)符合台灣中學實務, 變數量小;改 y[配課, 節次, 教室] 是為了不存在的需求付模型複雜度。
  • teacher_time_rule 牆鐘化(加節次表維度):v1 已定案「以該配課班級的節次表解讀」, 單節次表學校(絕大多數)無此問題;等真的出現「跨部授課教師 + 兩套節次表」的學校再改 schema。

依賴與順序總覽

1(E2E 日期,有死線)→ 2(ops 佇列,定容器架構)→ 3(部分排課三合一)
      → 4(學期複製)→ 5(小型加固批次)→ 發 v1.1
      

每項照既有流程:實作 → 全品質門檻(ruff/mypy/pytest;eslint/vue-tsc/build/vitest)→ Docker 重建 → E2E(現在 CI 也會擋)→ 真 PostgreSQL 實測 → commit/push → tasks.md 勾銷。

本頁由 docs/roadmap.md 自動產生(scripts/build_docs.py)。 要修改內容請改 Markdown 原始檔,不要直接編輯這份 HTML。
排課與調代課系統 · MIT 授權 · GitHub