# C4 — Implementation Plan

> 對應 design：[design-C4.md](design-C4.md)
> 級別：中
> 預估工時：BE 1 天

---

## Pre-flight 驗證

```bash
# 1. 確認 C3 已 ship（C4 嚴格依賴 C3 — Response Schema 已改 derived）
git log --oneline | grep -E "C3|2026.*range-migration" | head -3

# 2. 確認 OscalProjectStartRequest 仍在預期位置
grep -n "class OscalProjectStartRequest\|class ProjectUpdateRequestSchema" \
  api/project/serializers/project.py api/grc/serializers/project.py

# 3. 確認 start_oscal_project Step C 仍在 line 456-461
grep -n "Step C\|project_device_mapping_service" \
  app/project/service/oscal_project_service.py | head -5

# 4. 確認 update_project Section 3/5 仍在 line 367 / 432
grep -n "audit_systems\|devices.*替換\|稽核系統替換\|設備替換" \
  app/grc/service/project_service.py | head -10

# 5. 確認 DI 注入點
grep -rn "project_device_mapping_service\|project_information_system_domain_service" \
  di_containers/ --include="*.py" | head -10
```

---

## 任務清單

### T1 — `OscalProjectStartRequest` 拿掉 audit_systems + devices 欄位（BE）

**檔案**：`api/project/serializers/project.py:30`

**動作**：
- 刪除 line 40-43（audit_systems 欄位）
- 刪除 line 48-51（devices 欄位）
- 加註解說明 C4 移除背景（指 docs/features/FR-011.3-2605-ssp-edit-in-project/）
- `Meta.unknown = EXCLUDE` 維持（向後相容）

**對應 DTO 變動**：
- 若 `StartProjectDTO`（或對應 dataclass）有 `audit_systems` / `devices` 屬性 → 同步拿掉
- pre-flight 確認 DTO 位置

**Commit message**：`refactor(project-api): C4 拿掉 OscalProjectStartRequest 內 audit_systems + devices`

---

### T2 — `ProjectUpdateRequestSchema` 拿掉 audit_systems + devices 欄位（BE）

**檔案**：`api/grc/serializers/project.py:77`

**動作**：
- 刪除 line 95-99（audit_systems 欄位）
- 刪除 line 105-109（devices 欄位）
- docstring 補說明 C4 改動

**Commit message**：`refactor(grc-api): C4 拿掉 ProjectUpdateRequestSchema 內 audit_systems + devices`

---

### T3 — `oscal_project_service.start_oscal_project` 移除 Step C（BE）

**檔案**：`app/project/service/oscal_project_service.py`

**動作**：
1. 找 Step C（line 456-461 區段）+ 對應 audit_systems INSERT（pre-flight 找確切位置）
2. 整段刪除
3. `__init__` 拿掉 `project_device_mapping_service` 參數
4. `self.project_device_mapping_service` 屬性 + 所有引用 grep + 移除
5. 確認 `delete_by_project_id`（line 1100）也要移除（刪專案 cascade，C3 應已搬到 SSP CASCADE，C4 確認清理）

**DI 改動**：
- `di_containers/project/project_containers.py` — 移除 `project_device_mapping_service` wiring

**Commit message**：`refactor(oscal-project): C4 移除 start_oscal_project Step C device/audit_systems 寫入邏輯`

---

### T4 — `GrcProjectService.update_project` 移除 Section 3 + 5（BE）

**檔案**：`app/grc/service/project_service.py`

**動作**：
1. 找 Section 3（line 367+ 稽核系統替換）+ Section 5（line 432-444 設備替換）
2. 整段刪除
3. `__init__` 拿掉 `project_device_mapping_service` + `project_information_system_domain_service` 參數
4. `self._device_mapping_service` + `self._pis_domain_service` 屬性 + 所有引用 grep + 移除
5. 注意 line 264 `dto.audit_systems` 組裝邏輯由 C3 改 derived，C4 不動 (Response 不變)

**DI 改動**：
- `di_containers/grc/grc_containers.py` — 移除兩個 service wiring

**Commit message**：`refactor(grc-project): C4 移除 update_project audit_systems + devices 替換邏輯`

---

### T5 — 補強 ParticipantInputSchema role validation（BE，optional）

**檔案**：`api/project/serializers/project.py:19`

**改動前**：
```python
class ParticipantInputSchema(Schema):
    user_uid = fields.String(required=True)
    role = fields.String(load_default="viewer")
```

**改動後**：
```python
from marshmallow import validate

class ParticipantInputSchema(Schema):
    user_uid = fields.String(required=True)
    role = fields.String(
        load_default="viewer",
        validate=validate.OneOf(["manager", "reviewer", "auditor", "viewer"]),
    )
```

**Commit message**：`tweak(project-api): C4 ParticipantInputSchema.role 加 OneOf validation`

---

### T6 — Tests 更新（BE）

**可能受影響的測試**：
```bash
grep -rln "audit_systems\|devices=\|StartProjectDTO" tests/ | head
```

預期受影響：
- `tests/test_oscal_project_service.py`
- `tests/test_grc_project_service.py`
- `tests/test_project_routes.py`
- 任何 fixture 內帶 `audit_systems` / `devices` 的測試 case

**動作**：
- 修 fixture 拿掉這兩個 field
- 修 assertion 不再 expect device / audit_system 被寫入
- 加新 test case：建專案後 `dto.devices == []` `dto.audit_systems == []`（依賴 C3 改 derive）

**Commit message**：`test(project): C4 移除 audit_systems / devices 相關 test fixture + 加 derived empty case`

---

### T7 — Changelog

**檔案**：`docs/changelog/YYYY-MM-DD-refactor-project-crud-drop-scope.md`

```markdown
---
type: tweak
breaking: true
modules: [project, grc]
---

## 需求說明
C4：拿掉專案建立/編輯 API 內的 audit_systems / devices 欄位。
受評範圍改在 SSP 編輯頁維護（per-AP SSP），見 docs/features/FR-011.3-2605-ssp-edit-in-project/

## 變更範圍
### BE
- `api/project/serializers/project.py`：OscalProjectStartRequest 拿掉 audit_systems + devices
- `api/grc/serializers/project.py`：ProjectUpdateRequestSchema 拿掉 audit_systems + devices
- `app/project/service/oscal_project_service.py`：start_oscal_project Step C 移除
- `app/grc/service/project_service.py`：update_project Section 3+5 移除
- DI containers：拿掉 project_device_mapping_service / project_information_system_domain_service 注入

## 行為差異
| 情境 | 改前 | 改後 |
|------|------|------|
| POST /oscal-project/start 含 devices | 寫入 project_device_mapping | unknown=EXCLUDE 忽略，建專案成功但不寫入 |
| PUT /grc/project/<uid> 含 audit_systems | 替換 mappings | EXCLUDE 忽略 |
| GET /grc/project/<uid> | devices/audit_systems from project tables | 由 C3 改 derived from SSP（C4 不變動 response）|

## Breaking change 警示
- FE 必須同步 deploy 更新表單（C6）
- 既有用 audit_systems / devices field 的 caller 改用 SSP-scoped endpoints（C2）
```

---

## 測試規格

| 測試 | 內容 |
|------|------|
| Unit | OscalProjectStartRequest 不接受 audit_systems / devices（unknown=EXCLUDE 忽略）|
| Unit | start_oscal_project 不再呼叫 project_device_mapping_service |
| Unit | update_project 不再呼叫 device / pis service |
| Unit | DI container resolve OscalProjectService 不需要 project_device_mapping_service |
| Integration | E2E：POST /oscal-project/start 不含 devices → 建專案 OK + Response devices=[] |
| Integration | E2E：建專案後 → 進 SSP tab 加 device → 再 GET 專案 → Response devices 從 SSP derive 出來 |

---

## 完成標準（DoD）

- [ ] 2 個 Request Schema 拿掉 audit_systems + devices
- [ ] 2 個 Service 移除對應寫入邏輯
- [ ] DI 注入清理
- [ ] 既有 test 更新 + 新 test case 加入
- [ ] BE pytest 全綠
- [ ] Changelog 含 breaking change 警示
- [ ] README tracker (C4) 狀態更新

---

## 風險 / Rollback

| 風險 | 緩解 |
|------|------|
| FE 沒同步 deploy 仍送 audit_systems / devices | unknown=EXCLUDE 容錯 + log warn |
| 既有 caller (gh stat / dashboard) 依賴 Response 內 audit_systems | C3 已改 derived，Response shape 不變，caller 透明 |
| DI 解依賴後其他 service 還有引用 | pre-flight `grep -rn project_device_mapping_service` 找全 |

**Rollback**：純程式碼改動，`git revert` 即可。但若已 deploy 且 FE 表單同步更新後，rollback 會撞 FE 不送 fields 但 BE 不接受新 schema 的 mismatch。建議：FE deploy 前先 deploy BE。
