From eb9c7080afbf4760b32327b3f9419d6bc792a258 Mon Sep 17 00:00:00 2001 From: Hank Date: Wed, 14 Oct 2026 20:39:43 +0800 Subject: [PATCH 1/2] =?UTF-8?q?chore(claude):=20=E6=96=B0=E5=A2=9E=20AC=20?= =?UTF-8?q?=E5=B0=8D=E6=87=89=E6=B8=AC=E8=A9=A6=E8=A6=8F=E5=89=87=E8=88=87?= =?UTF-8?q?=20branch-and-propose=20skill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/skills/branch-and-propose/SKILL.md | 26 ++++++++++++++++++++++ CLAUDE.md | 6 +++-- 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .claude/skills/branch-and-propose/SKILL.md diff --git a/.claude/skills/branch-and-propose/SKILL.md b/.claude/skills/branch-and-propose/SKILL.md new file mode 100644 index 0000000..bc604a5 --- /dev/null +++ b/.claude/skills/branch-and-propose/SKILL.md @@ -0,0 +1,26 @@ +--- +name: branch-and-propose +description: 開始規劃新功能時觸發,自動從 master 同步並建立新分支,接著進入 OpenSpec 提案流程。當使用者說「規劃新功能」「開新的 spec」「開始做 XXX 功能」「幫我開一個新分支做 XXX」時使用。 +argument-hint: [功能名稱] +--- + +## Instructions + +1. 確認目前 git 狀態是否乾淨(`git status`);若有未提交的變更,先詢問使用者要 stash 還是先提交,不可直接覆蓋。 +2. 執行以下指令同步 master: + ``` + git checkout master + git pull VPS master + ``` +3. 若使用者的請求中沒有明確功能名稱,先詢問功能名稱,並轉換為符合團隊命名慣例的分支名稱(英文、kebab-case,例如 `feature/dive-log-export`)。 +4. 建立並切換新分支: + ``` + git checkout -b feature/<功能名稱> + ``` +5. 分支建立完成後,向使用者確認分支已就緒,接著**必須**執行 `/opsx:propose <功能名稱>`(`<功能名稱>` 沿用步驟 3 決定的名稱)建立本次功能的 spec 提案草稿,不要自行用其他方式產生 proposal。 +6. spec 草稿完成後,依照 CLAUDE.md 的「OpenSpec 工作流規則 → Task 執行前」清單,確認 Acceptance Criteria 是否已定義且對應測試任務,未對應者提醒使用者補上,不進入實作階段。 + +## Notes + +- 這個 skill 只負責「開分支 → 交接給 OpenSpec 流程」,不涉及 spec 內容本身的產出邏輯,避免與 CLAUDE.md 裡的 OpenSpec 規則重複維護。 +- 若專案的預設分支不是 `master`(例如 `main`),使用前請調整步驟 2、3 中的分支名稱。 diff --git a/CLAUDE.md b/CLAUDE.md index cd280f6..4ebf6ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# CLAUDE.MD — 全域開發規則 +# CLAUDE.MD ## 語言設定 @@ -21,6 +21,7 @@ - 禁止在未確認 spec 對應關係的情況下開始實作 - 禁止使用 raw SQL query(除非有明確說明理由) - 禁止在前端直接使用 `innerHTML` 渲染未經處理的使用者輸入 +- 禁止在 Acceptance Criteria 未對應測試任務的情況下開始實作 --- @@ -108,7 +109,7 @@ 每次執行 task 前,先確認: 1. 這個 task 對應哪一條 spec?(必須能指向具體文件與段落) -2. Acceptance Criteria 是否已定義且可測量? +2. Acceptance Criteria 是否已定義且可測量?每一條是否已對應至少一項測試任務(單元測試或 Feature 測試)?未對應者禁止進入實作階段。 3. 這個 task 完成後,哪些 spec 文件需要同步更新? ### Task 執行後 @@ -118,6 +119,7 @@ - 實作結果是否與 spec 描述一致 - 如有偏差,列出偏差點並詢問是否更新 spec - **必須告知 spec 同步狀態**:這個改動有沒有影響現有 spec 描述的行為?需要更新哪個文件? +- 測試是否涵蓋本次變更的 Acceptance Criteria?未涵蓋者需說明原因 --- -- 2.52.0 From 871c0546fd4030c8a7fff81392d745c54c8a8b6d Mon Sep 17 00:00:00 2001 From: Hank Date: Wed, 14 Oct 2026 21:58:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs(analysis):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=B0=88=E6=A1=88=E5=AF=A9=E8=A6=96=E5=BE=85=E8=BE=A6=E8=88=87?= =?UTF-8?q?=20project=20prompt=20=E6=94=B9=E5=96=84=E8=A8=88=E7=95=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-10-14-project-prompt-improvements.md | 128 ++++++++++++ .../2026-10-14-project-review-todo.md | 193 ++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 docs/analysis/2026-10-14-project-prompt-improvements.md create mode 100644 docs/analysis/2026-10-14-project-review-todo.md diff --git a/docs/analysis/2026-10-14-project-prompt-improvements.md b/docs/analysis/2026-10-14-project-prompt-improvements.md new file mode 100644 index 0000000..addd895 --- /dev/null +++ b/docs/analysis/2026-10-14-project-prompt-improvements.md @@ -0,0 +1,128 @@ +# Project Prompt/Skill/Subagent 改善計畫 + +> 日期:2026-10-14 +> 範圍:`CLAUDE.md`、`openspec/config.yaml`、`.windsurf/rules/`、`.claude/skills/`、`.claude/commands/opsx/`、subagent 定義。 +> 性質:工作流與文件調整,不涉及應用程式行為,不需走 OpenSpec change,以 `chore/` 分支提 PR 即可。 +> 搭配閱讀:《2026-10-14-project-review-todo.md》。 + +--- + +## 一、Project Prompt + +### 發現的問題 + +| # | 問題 | 影響 | 優先 | +|---|------|------|------| +| P1 | `openspec/config.yaml` 的 context 過期:寫「Admin Panel、預約/訂單系統、課程圖片上傳、教練審核流程」尚未實作,實際皆已完成並歸檔 | 每次 `/opsx:propose` 注入錯誤的專案狀態,AI 可能重做或誤判 | 高 | +| P2 | `config.yaml` 寫 localStorage key `token`、`coach_token`;實際 member 為 `sessionStorage` | 同上 | 高 | +| P3 | `.windsurf/rules/ponytail-lazy-senior-dev-mode.md` 要求「No abstractions、fewest files」,與 CLAUDE.md 的 SOLID、Service 層、DTO、依賴反轉直接衝突 | Windsurf 與 Claude Code 產出風格相反 | 高 | +| P4 | CLAUDE.md 未寫專案實際慣例(API 回應格式、目錄結構、Swagger 放 `app/Docs/`) | AI 需自行猜測 | 中 | +| P5 | 未寫驗證指令與格式化要求(`php artisan test`、`./vendor/bin/pint`) | 實作後沒有標準驗證步驟 | 中 | +| P6 | 未寫 Git/PR 流程(分支命名、commit 格式、PR 目標 remote、禁止直接 push master) | 目前只存在於 skill,一般對話不會觸發;push master 會直接觸發部署 | 高 | +| P7 | `config.yaml` 只有 `rules.tasks`,沒有 `rules.proposal`、`rules.specs`,無法在 spec 階段強制 AC 可測量 | CLAUDE.md 的「AC 必須對應測試」只靠 AI 自覺 | 中 | +| P8 | 沒有授權規範:新路由必須掛角色 middleware(見《project-review-todo》H1) | 漏洞的根因 | 高 | +| P9 | 安全確認清單未說明「答案要記錄在哪裡」 | 無法稽核 | 低 | +| P10 | CLAUDE.md 與 `config.yaml` 都有 OpenSpec 規則,且各寫各的 | 重複維護、容易不一致 | 低 | + +### 建議修改 + +#### 1. `openspec/config.yaml` +- [ ] 更新「已完成模組」:Admin Panel、預約與時段、即時訊息、評價與投票、通知、課程圖片、教練審核、S3 儲存、鎖定與限流、Token Refresh +- [ ] 更新「尚未實作」:金流整合、訂閱方案(`subscriptions`/`plans` 尚無 API) +- [ ] 修正前端儲存描述(以實際程式為準,改前先重新確認 coach 端) +- [ ] 加入 `rules.proposal`:必須列出受影響的主規格 +- [ ] 加入 `rules.specs`:每個 Requirement 至少一個 `#### Scenario`;Scenario 需可轉為測試 +- [ ] 加入 `rules.tasks`:每個 Scenario 至少對應一個 `[整合測試]` task,否則不得標記 apply-ready +- [ ] 加入 `rules.design`:觸及安全觸發條件時,必須附上「安全確認」段落(回應 P9) + +#### 2. `CLAUDE.md`(新增區塊,避免與 config.yaml 重複規則) +- [ ] **專案慣例**:API 回應 `{ status, message?, data }`、Controller 位於 `app/Http/Controllers/API/`、Swagger 註解位於 `app/Docs/` +- [ ] **驗證指令**:`php artisan test`、`./vendor/bin/pint`;說明測試預設使用 SQLite,涉及行鎖的測試需另行確認 +- [ ] **Git 流程**:分支前綴 `feat/`、`fix/`、`chore/`、`docs/`;commit 使用 Conventional Commits 加繁中描述;一律走 PR;**禁止直接 push master(會觸發自動部署)**;推送 remote 為 `vps` +- [ ] **授權規範**:新增路由必須掛角色 middleware,並附跨角色 403 測試 +- [ ] 以一句話指向 `openspec/config.yaml`,OpenSpec 細節規則只維護一份 + +#### 3. `.windsurf/rules/` +- [ ] 三選一,**需你決定**: + - 刪除 ponytail 規則,改為指向 CLAUDE.md(建議,單一事實來源) + - 保留但改寫,移除與 SOLID 衝突的條款 + - 保留,但明確標註「僅用於快速原型,不用於本專案正式程式」 +- [ ] 確認 `docs/STARTUP.md` 內容是否與 CLAUDE.md 重複 + +--- + +## 二、Skills + +### 現況 +- 5 個 skill:`openspec-explore`、`openspec-propose`、`openspec-apply-change`、`openspec-archive-change`(OpenSpec 官方產出,版本 1.3.1)及自訂的 `branch-and-propose` +- `.claude/commands/opsx/*` 與對應 skill 內容幾乎重複,僅差少數文句 + +### 發現的問題 + +| # | 問題 | 優先 | +|---|------|------| +| S1 | `openspec-apply-change` 沒有任何測試相關指令,與新增的「AC 必須對應測試」規則沒有連動 | 高 | +| S2 | `branch-and-propose` 使用 `git pull VPS master`,實際 remote 名稱為小寫 `vps`(Linux 區分大小寫) | 中 | +| S3 | `branch-and-propose` 未處理「目前不在 master」的情況,也未說明分支命名前綴規則(只舉 `feature/`,實際慣例有 `feat/`、`fix/`、`chore/`) | 中 | +| S4 | 沒有「實作完成 → 驗證 → 提 PR」的收尾 skill | 中 | +| S5 | `CLAUDE.md` 要求完成後回報 spec 同步狀態、重複邏輯、命名一致性、AC 測試涵蓋,目前沒有固定模板 | 中 | +| S6 | 安全確認清單未做成可載入的 skill | 低 | +| S7 | 新增 API 端點的固定步驟(路由、角色 middleware、驗證、Swagger、測試)每次重複 | 低 | +| S8 | `commands/opsx` 與 `skills/openspec-*` 重複,OpenSpec 升級時可能只更新其中一邊 | 低 | + +### 建議修改 + +- [ ] **修改 `openspec-apply-change`**:開始前檢查 tasks.md 中每個 Scenario/AC 是否有對應測試 task,缺漏則停止並回報 +- [ ] **修正 `branch-and-propose`**:`vps` 小寫、處理非 master 起點、說明分支前綴規則 +- [ ] **新增 `verify-and-report`**:執行 `php artisan test`、`pint`,並依 CLAUDE.md 輸出固定格式報告(spec 同步狀態、偏差點、重複邏輯、命名一致性、AC 測試涵蓋) +- [ ] **新增 `finish-and-pr`**:確認測試通過、依 Conventional Commits 提交、推送分支(不推 master)、提示開 PR;歸檔另走 `openspec-archive-change` +- [ ] **新增 `security-checklist`**:從 CLAUDE.md 抽出必答問題,碰到五類觸發條件時載入 +- [ ] **新增 `add-endpoint`**:新增 API 端點的檢核流程 +- [ ] 決定 `commands/opsx` 與 `skills/openspec-*` 是否保留雙份(由 OpenSpec CLI 產生,建議不手動合併,只記錄於升級流程) + +--- + +## 三、Subagent + +### 現況 +專案層級沒有任何 subagent 定義(`.pi/agents`、`.claude/agents` 皆不存在)。 + +### 建議角色 + +| 角色 | 模式 | 建議模型層級/思考 | 用途 | +|------|------|--------------------|------| +| `spec-auditor` | 唯讀 | 中階/medium | 檢查 proposal、tasks 的 AC 是否對應測試,是否與主規格衝突 | +| `security-reviewer` | 唯讀 | 頂階/high | 依 CLAUDE.md 安全清單審查 diff(輸入驗證、授權、N+1、XSS) | +| `test-runner` | 執行 | 快速/low | 跑測試並整理失敗原因,註明 SQLite 限制 | +| `backend-implementer` | 寫入 | 中階/medium | 依 tasks.md 的 `[後端]` 項目實作 | +| `frontend-implementer` | 寫入 | 中階/medium | 依 `[前端]` 項目實作 | +| `spec-syncer` | 寫入 | 中階/low | 歸檔時把 delta 同步回 `openspec/specs/` | + +### 並行規則 +- 唯讀角色可隨時並行 +- `backend-implementer` 與 `frontend-implementer` 僅在 design.md 已定義 API 契約且檔案不重疊時並行,各用獨立 worktree +- `test-runner` 必須在實作完成後序列執行 +- 所有寫入型 child 預設**不 push、不 merge**,由主 agent 驗證 diff 後整合 + +### 待辦 +- [ ] 決定 subagent 定義的存放位置(專案 `.pi/agents/` 或 `.claude/agents/`);**需你確認**目前主要使用哪個工具鏈 +- [ ] 先建立兩個唯讀角色:`spec-auditor`、`security-reviewer` +- [ ] 試用一輪後再決定是否建立寫入型角色 + +--- + +## 四、建議執行順序 + +1. 更新 `openspec/config.yaml` context 與 rules(P1、P2、P7) +2. 決定並處理 `.windsurf` 衝突(P3) +3. 補 CLAUDE.md:Git 流程、授權規範、驗證指令、專案慣例(P4、P5、P6、P8) +4. 修 `branch-and-propose`、`openspec-apply-change`(S1、S2、S3) +5. 新增 `verify-and-report`、`finish-and-pr`(S4、S5) +6. 建立唯讀 subagent(spec-auditor、security-reviewer) +7. 視需要補 `security-checklist`、`add-endpoint` + +## 需要你決定的事項 + +1. `.windsurf` ponytail 規則:刪除、改寫、還是標註用途? +2. subagent 存放位置與主要工具鏈(Claude Code、Pi、Windsurf) +3. `config.yaml` 中前端 token 儲存的描述:以現況為準,還是等 M4 統一後再寫? diff --git a/docs/analysis/2026-10-14-project-review-todo.md b/docs/analysis/2026-10-14-project-review-todo.md new file mode 100644 index 0000000..5f3e596 --- /dev/null +++ b/docs/analysis/2026-10-14-project-review-todo.md @@ -0,0 +1,193 @@ +# CFDivePlatform 專案審視:高/中優先待辦 + +> 審視日期:2026-10-14 +> 範圍:後端授權、部署設定、程式結構、資料一致性。 +> 低優先項目未列入本文(分頁、範本殘留檔、CHANGELOG 等),需要時另行整理。 +> 搭配閱讀:《2026-06-11-spec-implementation-audit.md》。 +> 實作原則:以下每一項只要涉及行為變更,都必須先走 OpenSpec(`/opsx:propose`),AC 對應測試後才可實作。 + +--- + +## 總覽 + +| # | 優先 | 項目 | 是否走 OpenSpec | 建議 change 名稱 | +|---|------|------|----------------|------------------| +| H1 | 🔴 高 | member/provider 路由缺角色 middleware | 是 | `add-role-middleware` | +| H2 | 🔴 高 | 正式環境 `APP_DEBUG` 與預設值風險 | 否(設定確認)+ 視情況改 `.env.example` | — | +| M1 | 🟡 中 | AuthController 過大、無 FormRequest/Service 層 | 是 | `refactor-auth-controller` | +| M2 | 🟡 中 | 預約名額邏輯重複 | 是 | `extract-booking-service` | +| M3 | 🟡 中 | 通知同步發送且例外被吞掉 | 是 | `queue-booking-notifications` | +| M4 | 🟡 中 | 前端 token 儲存不一致(admin 用 localStorage) | 是 | `unify-frontend-token-storage` | +| M5 | 🟡 中 | CORS 在正式環境仍允許 localhost | 是(小) | `restrict-cors-by-env` | +| M6 | 🟡 中 | CI 使用 SQLite,鎖定/超賣測試可能假綠 | 是 | `ci-mysql-test-job` | + +建議順序:H1 → H2 → M6 → M5 → M3 → M2 → M1 → M4。 +理由:H1 是安全漏洞;M6 先建立可信的測試基礎,後續重構(M1、M2)才有安全網。 + +--- + +## H1 member/provider 路由缺角色 middleware + +**現況** +`routes/api.php` 中 `member` 與 `provider` 兩個群組只有 `auth:sanctum`,沒有角色檢查;只有 `admin` 群組有 `EnsureAdmin`。 + +**已確認的影響** +- 任何已登入的 member 或 admin 可呼叫 `POST /api/provider/offers`、`/provider/schedules`、`/provider/offers/{id}/images`,`provider_id` 會被設成呼叫者 id,產生非教練帳號的課程。 +- 任何 provider 或 admin 可呼叫 `POST /api/member/bookings` 建立預約。 +- 目前只有 `ReviewController`、refresh 端點自己檢查角色,其餘端點無防護。 + +**待辦** +- [ ] 新增 `EnsureMember`、`EnsureProvider` middleware(仿 `app/Http/Middleware/EnsureAdmin.php`),並在 `bootstrap/app.php` 註冊 alias +- [ ] 套用到 `member`、`provider` 路由群組 +- [ ] 通知、即時訊息這類「member + provider 共用」群組維持現狀,但需確認 admin 是否應被排除 +- [ ] 新增 Feature 測試(仿 `tests/Feature/AdminEndpointAuthTest.php`):每個角色 × 每組路由,期望 403 +- [ ] 更新 spec:`provider-auth`、`admin-auth`;視需要新增 `role-authorization` spec +- [ ] 更新 Swagger 註解(`app/Docs/*`)的 403 回應 + +**驗收條件(草案,需轉為 spec Scenario)** +1. member 呼叫任一 `/api/provider/*` 回 403 +2. provider 呼叫任一 `/api/member/*` 回 403 +3. admin 呼叫 `/api/member/*`、`/api/provider/*` 回 403 +4. 既有合法角色的行為不變(既有測試全綠) + +**安全確認(CLAUDE.md 必答)** +- 權限檢查層級:middleware(路由層),Controller 內既有所有權檢查保留 +- 未授權觸發可能性:修正後不可 + +--- + +## H2 正式環境 `APP_DEBUG` 與預設值 + +**現況** +- 本機 `.env`:`APP_ENV=local`、`APP_DEBUG=true` +- `.env.example`:`APP_DEBUG=true`、`APP_ENV=local` +- `docker-compose.yml` 以 `env_file: .env` 注入容器 + +**風險** +若 VPS 上 `.env` 沿用相同值,錯誤頁會洩漏堆疊追蹤、環境變數與 SQL。 + +**待辦** +- [ ] **需要你確認**:登入 VPS 檢查實際 `.env` 的 `APP_DEBUG`、`APP_ENV`(我無法從此環境確認) +- [ ] 確認 `config:cache` 後的實際值:`docker compose exec app php artisan tinker --execute="dump(config('app.debug'));"` +- [ ] `.env.example` 加註解說明正式環境必須 `APP_DEBUG=false` +- [ ] 評估是否在 deploy.yml 加入啟動檢查:`APP_ENV=production` 且 `APP_DEBUG` 為 false,否則部署失敗 +- [ ] 本機 `.env` 中的 DB 密碼為弱密碼(`cfdivepass`),確認正式環境未使用 + +**備註** +這項大多是設定確認,不一定需要 OpenSpec change;若加入部署檢查則屬 `env-cloud-annotations` spec 範圍。 + +--- + +## M1 AuthController 過大,缺 FormRequest 與 Service 層 + +**現況** +- `app/Http/Controllers/API/AuthController.php` 1136 行 +- 三種角色的 register/login/logout/profile/change-password/refresh 大量複製 +- `app/Http/Requests`、`app/Services` 目錄不存在,驗證與業務邏輯都在 Controller +- 已有 `commonLogout`、`commonChangePassword` 等雛形,但 `refreshMember`/`refreshProvider`/`refreshAdmin` 幾乎相同 + +**違反的 CLAUDE.md 規則** +單一職責、Function 超過 20 行需說明、Controller 只處理資源、參數過多應封裝 DTO。 + +**待辦** +- [ ] 先補足現有行為的測試(`AuthLoginTest`、`AuthLockoutTest`、`TokenRefreshTest` 已有基礎),重構前先確認覆蓋率 +- [ ] 拆分:依角色或依功能拆成多個 Controller(例如 `MemberAuthController`),共用邏輯抽成 `AuthService` +- [ ] 引入 FormRequest(`RegisterMemberRequest` 等) +- [ ] 合併三個 refresh 方法 +- [ ] 重構期間 API 路徑、回應格式、Swagger 輸出**不得改變** +- [ ] 重構完成後 `app/Docs/AuthApiDoc.php`(1261 行)註解位置需一併檢視 + +**風險** +改動面大,務必在 M6(CI MySQL)之後進行,並分多個小 PR。 + +--- + +## M2 預約名額邏輯重複 + +**現況** +「取消後遞減 `current_participants`,若由 Full 變回 Open」的邏輯同時出現在: +- `MemberBookingController::destroy` +- `ProviderBookingController::cancel` +- `AdminBookingController`(需確認) + +同時 `MemberBookingController::store` 約 70 行,包含驗證、鎖定、建立、通知。 + +**待辦** +- [ ] 抽出 `BookingService`(或 `CourseSchedule` 的 `releaseSpots()` 方法)集中名額增減與狀態轉換 +- [ ] 確認所有取消路徑使用同一入口 +- [ ] 保留 `BookingOversellTest`、`BookingLifecycleTest` 全綠 +- [ ] 補單元測試:名額釋放、Full → Open 的邊界 + +--- + +## M3 通知同步發送,例外被吞掉 + +**現況** +`MemberBookingController`、`ProviderBookingController` 多處以 `try { notify() } catch (\Throwable) { Log::error }` 發送通知。 + +**風險** +- 若 Notification 未實作 `ShouldQueue`,會在請求內同步寄信,拖慢回應 +- 失敗只留 log,沒有重試,也沒有告警(Sentry 不會收到被吞掉的例外) + +**待辦** +- [ ] 檢查 `app/Notifications/*` 是否皆實作 `ShouldQueue` +- [ ] 改為交由 queue worker 處理(`cfdive-queue` 容器已存在),並設定 `tries`/`backoff` +- [ ] 移除 Controller 內的 try/catch,改用 queue 失敗處理;必要時以 `report($e)` 送 Sentry +- [ ] 更新 `NotificationTriggerTest` 使用 `Queue::fake()` 或 `Notification::fake()` +- [ ] 更新 spec:`notification-core`、`notification-triggers`、`notification-email` + +--- + +## M4 前端 token 儲存不一致 + +**現況** +- member:`sessionStorage`(`frontend/src/api/axios.js`) +- admin:`localStorage`(`frontend/src/api/adminAxios.js`) +- `openspec/config.yaml` 仍寫 localStorage(見另一份 prompt 文件) + +**風險** +管理員權限最高,卻用持久儲存,XSS 時外洩影響更大。好消息是前端目前沒有 `v-html` 或 `innerHTML`。 + +**待辦** +- [ ] 確認 coach 端(`coachAxios.js`)實際使用哪種儲存 +- [ ] 決定統一策略(至少 admin 改為 `sessionStorage`) +- [ ] 評估是否改用 HttpOnly Cookie(需搭配 Sanctum SPA 模式,改動大,另案評估) +- [ ] 更新 `member-portal-ui`、`coach-portal-ui`、`admin-panel-ui` spec 中關於儲存的描述 + +--- + +## M5 CORS 在正式環境仍允許 localhost + +**現況** +`config/cors.php` 的 `allowed_origins` 固定包含 `http://localhost:5173` 與 `http://127.0.0.1:5173`。 + +**待辦** +- [ ] 改為依 `APP_ENV` 決定:local 才加入 localhost,production 僅允許 `FRONTEND_URL` +- [ ] 補一個 Feature 測試驗證 production 設定下的 CORS 標頭 +- [ ] 同步檢查 Reverb(WebSocket)的 allowed origins 設定 + +--- + +## M6 CI 使用 SQLite,鎖定/超賣測試可能假綠 + +**現況** +- `.gitea/workflows/test.yml` 以 `cp .env.example .env` 執行,`.env.example` 為 `DB_CONNECTION=sqlite` +- `BookingOversellTest` 等測試依賴 `lockForUpdate()`,SQLite 不支援行鎖,該方法在 SQLite 上實質無效 +- 另外 SQLite 與 MySQL 在 enum、JSON、時間函式上行為不同 + +**風險** +CI 綠燈不代表正式環境(MySQL)行為正確,尤其是併發情境。 + +**待辦** +- [ ] 在 CI 增加 MySQL service,至少讓預約、超賣相關測試在 MySQL 上跑一次 +- [ ] 檢查 `phpunit.xml` 的 DB 設定,確認 CI 與本機一致 +- [ ] 評估 `BookingOversellTest` 是否真的測到併發(單程序測試無法驗證行鎖,可能需要多程序測試或接受其限制並在文件註明) +- [ ] `deploy.yml` 與 `test.yml` 目前互相獨立;評估是否讓部署依賴測試通過 +- [ ] 更新 spec:`feature-test-coverage` 或新增 `ci-pipeline` spec + +--- + +## 其他提醒 + +- Git remote `vps` 的 URL 內含明文 Gitea token(存在 `.git/config`,未進版控)。建議輪換 token,改用 credential helper 或 SSH。 +- 本文件為待辦清單,不代表已排入開發;每項啟動前仍需依 CLAUDE.md 完成「Task 執行前」檢查。 -- 2.52.0