# Session 3 交接 Prompt — C2 完工，接續 C3 / C5

> 2026-05-22 Session 2 完成後建立。貼以下 ``` 框內容開啟 Session 3。

---

```
你正在接手 SSP 在專案內直接編輯 — Phase C Session 3。

## Session 2 完成狀況（已 ship）

### C2 — SSP-scoped endpoints 補齊（19 endpoint 全數 shipped）

| Batch | Commit 範圍 | Endpoint 數 | Changelog |
|-------|------------|------------|-----------|
| C2.1 | `603eabe..b0047cf` | 11 | docs/changelog/2026-05-22-feat-ssp-project-endpoints-c2-1.md |
| C2.2 | `fa8f8dc..df87826` | 8 | docs/changelog/2026-05-22-feat-ssp-project-endpoints-c2-2.md |

完整 commit 序列：
- `603eabe` T1: SspProjectResolver + SspPermissionChecker + 3 個 error code
- `a51883e` T2.1: PartyContextService 抽 base
- `8435f22` T2.2: SspResourcesContextService 抽 base
- `b9ac23c` T3: /ssp/<uid>/parties 4 endpoint
- `4ad2f2f` T4: /ssp/<uid>/ssp-resources 4 endpoint
- `d8e0800` T5: /ssp/<uid>/system-characteristic 2 endpoint
- `b0047cf` T6: /project/<uid>/current-ssp-uid helper
- `6d4d24b` C2.1 changelog + tracker
- `fa8f8dc` T10: /ssp/<uid>/leveraged 4 endpoint
- `6c72bf7` T8: excel_import 加 source_type='ssp'
- `df87826` T9: /ssp/<uid>/excel-import/* 4 endpoint
- `0b386f9` C2.2 changelog + tracker

### 19 個新 endpoint 一覽

```
# C2.1 SSP-scoped 編輯
GET    /ssp/<ssp_uid>/parties
POST   /ssp/<ssp_uid>/parties
PUT    /ssp/<ssp_uid>/parties/<party_uid>
DELETE /ssp/<ssp_uid>/parties/<party_uid>
GET    /ssp/<ssp_uid>/ssp-resources
POST   /ssp/<ssp_uid>/ssp-resources/items
PUT    /ssp/<ssp_uid>/ssp-resources/items/<item_uid>
DELETE /ssp/<ssp_uid>/ssp-resources/items/<item_uid>
GET    /ssp/<ssp_uid>/system-characteristic
PUT    /ssp/<ssp_uid>/system-characteristic
GET    /project/<uid>/current-ssp-uid

# C2.2 leveraged + excel-import
GET    /ssp/<ssp_uid>/leveraged
POST   /ssp/<ssp_uid>/leveraged
PUT    /ssp/<ssp_uid>/leveraged/<item_uid>
DELETE /ssp/<ssp_uid>/leveraged/<item_uid>
POST   /ssp/<ssp_uid>/excel-import/upload
GET    /ssp/<ssp_uid>/excel-import/<parse_uid>
DELETE /ssp/<ssp_uid>/excel-import/<parse_uid>
POST   /ssp/<ssp_uid>/excel-import/<parse_uid>/confirm
```

### 測試現況

- C2 新 unit test 共 104 個 全綠
- 既有 SSP excel import / excel_parser / MF / party 等 regression 全綠
- 1 個 pre-existing fail `test_write_parties_refreshes_existing_party_in_place` 與本批無關（之前就壞）

## Phase C 整體狀態

| Phase | 主題 | 狀態 |
|-------|------|------|
| C1 | OSCAL SSP 角色 master | ✅ shipped (`381d3ec`) |
| C7 | 06_外部利用服務 sheet | ✅ shipped (`e7f29e3`) |
| C8 | role label 清理 | ✅ shipped (`be1d286`) |
| **C2** | **SSP-scoped endpoints** | **✅ shipped (本期完成)** |
| **C3** | **受評範圍歸屬遷移**（**重型**） | **spec-ready，下一步** |
| C4 | 專案 CRUD 改造（拿掉 audit_systems / devices） | spec-ready，依賴 C3 |
| C5 | FE SSP tab | spec-ready，C2 已就緒可開工 |
| C6 | 移除專案設定 UI 受評範圍 | spec-ready |

## 本 Session 目標（請 user 確認方向）

候選方向（任選或合併）：

### 方向 A：開始 C3（重型 refactor）

C3 範圍：廢除 `project_device_mapping` / `project_information_systems`，受評範圍改 derived from SSP items。

開工前必做：
- 讀 `docs/features/FR-011.3-2605-ssp-edit-in-project/design-C3.md`（兩階段 PR1 + PR2）
- 讀 `docs/features/FR-011.3-2605-ssp-edit-in-project/implementation-plan-C3.md`
- 注意：C3 不可獨立 ship — 必須跟 C4 同 PR / 同 release，否則前端會撞空 schema（tracker 反斷層機制 §4）

### 方向 B：開始 C5（FE SSP tab）

C5 可獨立開工，純前端：
- 切到 FE repo：`~/Projects/Billows/Audit-Manager/compliance-manager-fe/`
- 讀 `docs/features/FR-011.3-2605-ssp-edit-in-project/design-C5.md` + `implementation-plan-C5.md`
- 依賴 C2 已 ship 的 19 個 endpoint

### 方向 C：清理 C2 延後項

兩個延後 follow-up：
1. **multi-round SSP `get_one` bug 修補**（既有 issue，非 C2 引入）
   - 位置：`app/oscal/service/ssp_excel_import_app_service.py:_resolve_existing_ssp_shell()`
   - 問題：用 `get_one(SystemSecurityPlanQueryEntity(profile_id=...))` 在 SSP versioning 多輪場景會撞 multiple match
   - 影響：MF-scoped excel update flow 在多輪 SSP 場景失準
   - SSP-scoped 路徑（C2.2 T8 ship）已避開此 bug
2. **`PUT /ssp/<uid>/excel-import/<parse_uid>/decisions` partial-save endpoint**
   - design-C2.md §4.5 提的第 5 個 excel-import endpoint
   - 目前走「decisions 隨 confirm body 一次性傳入」已能滿足 day-1
   - 需要：app service 新 `save_decisions` method + parse_job 狀態擴充

## 重要規範變更（**Session 2 期間新增**）

CLAUDE.md 加了**禁止自行切換 branch**：
> 任何 git checkout <branch> / git switch 操作一律不執行，永遠在使用者當下的 current branch 工作。

Session 2 期間發生過一次工作目錄意外切到 phase2 branch 的事件（疑似外部觸發），用 git stash / pop 把 T6 改動搬回 phase3 才順利完成。若再次發生，**不要自己 checkout**，停下問 user。

## 環境 / 規範速查

### Branch
`feature/ssp-import-export-phase3`（目前所在；**Session 3 不要切換**）

### Working tree dirt（與本批無關）
- `pyproject.toml` — dev-only jedi-oscal path dep（按規範不該 commit）
- `docs/conversation-history/2026-05-22/ssp-import-export-phase2/part-*.md` — IDE 開檔導致的非語義變化
- `docs/features/ssp-import-export-phase2.zip` — untracked 從早期 session 留下

### DB 連線
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev

### Dev 帳號
- manual：blsadmin / Billows@123!
- pytest：blsit / Billows@123!

### BE log
`log/app.log`（專案根目錄）

### 必讀文件（接手前掃一遍）

1. CLAUDE.md（會自動載入 — 注意新增的「禁止自行切換 branch」規範）
2. `docs/features/FR-011.3-2605-ssp-edit-in-project/README.md`（tracker — 必看 C3 / C5 行的狀態）
3. `docs/features/FR-011.3-2605-ssp-edit-in-project/design-C3.md`（若走方向 A）
4. `docs/features/FR-011.3-2605-ssp-edit-in-project/design-C5.md`（若走方向 B）

## 開工建議流程

1. **先問 user 方向 A / B / C**（不要自己決定 C3 vs C5）
2. 跑 pre-flight：
   ```bash
   # 確認 C2 endpoints 都 register OK
   grep -rn "ssp_uid.*string" api/oscal/__init__.py api/grc/__init__.py | wc -l
   # 預期：12+（C2.1 11 + C2.2 8 個 SSP-scoped url + current-ssp-uid 在 grc）

   # 確認 19 個新 endpoint 都正常 import
   python -c "from api.oscal import create_module; create_module()"
   ```
3. 依方向開新任務清單（TaskCreate）

## C2.1 / C2.2 留下可重用的基礎設施

接下來的 phase（C3 / C4 / C5）若有 SSP 權限 / project 反查需求，可直接用：

- `domain/oscal/service/ssp_project_resolver.py`
  - `SspProjectResolver.resolve(ssp_uid)` → `SspProjectContext(ssp, ap, project)`
  - `NON_EDITABLE_AP_STATUSES = (CLOSED, ARCHIVED)`
- `common/middleware/permission/ssp_permission.py`
  - `SspPermissionChecker.require_participant(ssp_uid)` → 任一 participant 通過
  - `SspPermissionChecker.require_manager(ssp_uid)` → manager + AP 可編輯
- `app/oscal/service/party_context_service.py` — OSCAL party CRUD 接 (context_type, context_id)
- `app/oscal/service/ssp_resources_context_service.py` — devices / info_systems CRUD 接 ssp_id
- `app/oscal/service/ssp_leveraged_context_service.py` — leveraged-authorization CRUD 接 ssp_id

新 error code（C2 期間加的）：
- `GRC_NOT_PROJECT_MANAGER = GRC_403051`
- `GRC_INVALID_OSCAL_ROLE = GRC_400076`
- `GRC_SSP_EDIT_AP_NOT_ACTIVE = GRC_412028`
```

---

## 補充：Session 2 期間關鍵決策

寫進 handoff 但 Session 3 可選擇性回顧：

1. **Error code 序號全新編**：plan 提的序號（404010 / 412001 / 400015 / 403005 / 404011 / 404012）皆已被佔 — 改用 404012 等既有 + 新編 403051 / 400076 / 412028
2. **party `role` 維持單值**：與既有 MF API 對等；design 提的 `role_ids[]` 多值延後
3. **`current-ssp-uid` URL 用 `/project/<uid>/` 單數**：沿用 grc routes 既有命名（plan 提 `/projects/<...>/`）
4. **AP 可編輯定義放寬**：`status not in (CLOSED, ARCHIVED)`，比 design 的 `status == 'active'` 寬鬆
5. **C2.2 multi-round bug 隔離**：既有 `_resolve_existing_ssp_shell(mf_uid)` 在多輪 SSP 用 `get_one(profile_id=...)` 撞 multiple match — SSP-scoped 路徑直接用 ssp_uid 避開
6. **leveraged 拆獨立 service**：共用 table 但欄位語意不同（party_uuid + date_authorized + purpose），拆 `SspLeveragedContextService`
7. **`PUT /<parse_uid>/decisions` partial-save 延後**：design 提但目前 service 走「decisions 隨 confirm body 一次性傳入」夠用
8. **借用 `ModuleFrameWriteStrategy` 內部 helpers**：`_upsert_party` / `_upsert_responsible_party` 不綁定 context_type，可跨 context 重用 — SSP path 借來避免複製 50 行 upsert 邏輯
