# Phase A0 Implementation Plan — 既有 `system_security_plan_system_implementations` 表擴充

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal**：在既有 `oscal.system_security_plan_system_implementations` 表 + jedi-oscal 對應 stack 上加 9 個新欄位、改 1 欄 nullable、enum +1 值、補 index，使其能服 Phase 2 後續所有 phase 的匯入 / 匯出需求。

**Architecture**：DDD 嚴格分層擴充 — DB schema migration 在主專案 `scripts/sql/` 內以 `cmmgr` 帳號執行（避 RLS）；jedi-oscal 套件 8 個層（ORM / entity / query_entity / repo interface + impl / mapper / yaml_mapper / DTO / enum）平行擴充。dev 期走 **poetry path dependency** 不立即發版，feature 整體完成才一次性 bump 版本推 Nexus。

**Tech Stack**：Python 3.11 / SQLAlchemy 2.0 / PostgreSQL / pytest / poetry（path dep）/ jedi-oscal local source

**Spec**：[design.md](design.md)（A0 SDD 收斂 10 個 brainstorm 問題）

**Branch**：`feature/ssp-import-export-phase2`（已 checkout）

**跨 repo 範圍**：
- compliance-manager-be（主）：SQL migration + 觸 jedi-oscal path dep + 整合測試
- jedi-oscal 套件：ORM / entity / repo / mapper / DTO / enum / yaml_mapper

---

## 全域實作規範（每個 task 都要遵守）

1. **TDD**：每個有可測 unit 的 task — 先寫 failing test → 確認 fail → 實作最小可過 → 確認 pass → commit
2. **顯式 git add**：每 commit 用 `git add <file1> <file2>`，**禁用 `-am` / `-A`**（避免 sweep 不相關修改）
3. **CLAUDE.md commit 規範**：commit message 含 `Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>`
4. **不自動 publish jedi-oscal**：所有套件 commit 留在 local，feature 完工再正式 bump 版本（CLAUDE.md「jedi-* 進版流程」）
5. **不下 `poetry lock`**：用 `poetry update jedi-oscal`（CLAUDE.md「依賴更新一律 poetry update」）
6. **SQL migration 用 `cmmgr`**（不用 `cm_app`，避 RLS）
7. **改 BE service 後提醒 user 重啟**（BE 沒 hot reload）

---

## File Structure

### compliance-manager-be（主專案）

| 動作 | 路徑 | 說明 |
|------|-----|------|
| Modify | `pyproject.toml` | 取消 jedi-oscal path dep 註解，pin 0.0.16 改 comment |
| Create | `scripts/sql/2026-05-18-extend-ssp-system-implementations.sql` | DB schema + 140 筆資料 migration |
| Create | `tests/test_ssp_system_implementation_regression.py` | 既有 caller smoke regression test |
| Create | `docs/changelog/2026-05-18-tweak-ssp-system-impl-extension.md` | A0 變更紀錄 |
| Modify | `docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md` | A0 row 狀態 → shipped + commit hash |
| Modify | `docs/features/FR-011.2-2605-ssp-import-export-phase2/requirement-understanding.md` | A0 段加 `✅ shipped` 標記 |

### jedi-oscal 套件（`~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/`）

| 動作 | 路徑 | 說明 |
|------|-----|------|
| Modify | `jedi_oscal/common/enum/code_enum.py` | `SystemImplementationType` 加 `LEVERAGED_AUTHORIZATION` |
| Modify | `jedi_oscal/infra/model/ssp/ssp_system_implementation.py` | 加 9 個 `Mapped[]` column + 改 system_security_plan_id nullable + 加 3 個 index |
| Modify | `jedi_oscal/domain/entity/ssp/ssp_system_implementation_entity.py` | 加 9 個 attribute |
| Modify | `jedi_oscal/domain/entity/ssp/ssp_system_implementation_query_entity.py` | 加 query 篩選欄位 |
| Modify | `jedi_oscal/infra/repository/ssp/ssp_system_implementation_repo_impl.py` | 加 `list_by_scope` / `find_by_device_id` / `find_by_information_system_id` |
| Modify | `jedi_oscal/domain/repository/ssp/system_implementation_repo.py` | interface 加對應 method |
| Modify | `jedi_oscal/infra/mapper/ssp/system_implementation_mapper.py` | 新欄位 entity ↔ model 雙向 mapping |
| Modify | `jedi_oscal/app/dto/ssp/ssp_system_implementation_dto.py` | 加 9 個欄位 |
| Modify | `jedi_oscal/infra/mapper/ssp/ssp_yaml_mapper.py` | 依 implementation_type 分支序列化 |
| Create | `tests/test_ssp_system_implementation_extension.py` | 9 欄位 + 3 query method + enum + mapper round-trip 整合測試 |

---

## Task 0: Pre-Flight Verification

開工前驗證所有 design.md §9 假設仍成立 — 從 brainstorm 到實作中間環境可能已變。

**Files：** （無，只是 verify 動作）

- [ ] **Step 1: Verify jedi-oscal ORM 結構**

```bash
head -90 ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/infra/model/ssp/ssp_system_implementation.py
```

Expected: class `OscalSystemSecurityPlanSystemImplementation`、`__tablename__ = "system_security_plan_system_implementations"`、`system_security_plan_id` 是 NOT NULL `ForeignKey`、有 `name` / `description` / `implementation_type` / `responsible_party` 6 欄位。

- [ ] **Step 2: Verify enum 既有 6 個值**

```bash
grep -A 8 "class SystemImplementationType" ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/common/enum/code_enum.py
```

Expected: `SYSTEM` / `SUBSYSTEM` / `SERVICE` / `COMPONENT` / `HARDWARE` / `SOFTWARE` 6 個值。

- [ ] **Step 3: Verify DB 140 筆 hardware 資料**

```bash
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c "SELECT implementation_type, COUNT(*) FROM oscal.system_security_plan_system_implementations GROUP BY implementation_type;"
```

Expected: `hardware | 140`。**若數字不同，停下確認原因再開工。**

- [ ] **Step 4: Verify 既有 caller 仍存在**

```bash
ls ~/Projects/Billows/Audit-Manager/compliance-manager-be/app/associations/service/project_device_mapping_service.py
ls ~/Projects/Billows/Audit-Manager/compliance-manager-be/app/oscal/service/ssp_versioning_service.py
```

Expected: 兩個檔都存在。

- [ ] **Step 5: Verify pyproject.toml jedi-oscal pin**

```bash
grep "jedi-oscal" ~/Projects/Billows/Audit-Manager/compliance-manager-be/pyproject.toml
```

Expected: `"jedi-oscal==0.0.16"`（active 行）+ commented out path line。**若版本已不是 0.0.16，記錄當前版本後繼續。**

- [ ] **Step 6: Verify branch 正確**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be && git branch --show-current
```

Expected: `feature/ssp-import-export-phase2`。

---

## Task 1: 切換 jedi-oscal 為 Poetry Path Dependency

**目的**：dev 期改套件源碼直接生效，避免每改 bump 版本（CLAUDE.md「改套件先走 poetry path dependency」）。

**Files：**
- Modify: `compliance-manager-be/pyproject.toml`

- [ ] **Step 1: 編輯 pyproject.toml**

註解掉 active 的 pin 行，取消註解 path 行：

```toml
# jedi-oscal 改 path dep（dev only，feature 完成還原）
# "jedi-oscal==0.0.16",
jedi-oscal = { path = "/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-oscal", develop = true }
```

- [ ] **Step 2: 跑 poetry update**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
poetry update jedi-oscal
```

Expected: 安裝完成，無錯誤。

- [ ] **Step 3: Verify import 仍可用**

```bash
poetry run python -c "from jedi_oscal.common.enum.code_enum import SystemImplementationType; print(list(SystemImplementationType))"
```

Expected: 印出 6 個 enum 值。

- [ ] **Step 4: 此步驟不 commit**

> ⚠️ `pyproject.toml` 的 path dep 改動屬於 **dev-only**，**不入 commit**（CLAUDE.md memory）。整個 A0 feature 完工 + smoke test 通過後，最後一個 task 才還原 pin Nexus 版本一起 commit。

---

## Task 2: jedi-oscal Enum 擴充

**目的**：加 `LEVERAGED_AUTHORIZATION = "leveraged-authorization"` 一個值。

**Files：**
- Modify: `jedi-oscal/jedi_oscal/common/enum/code_enum.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`（首次建立此測試檔）

- [ ] **Step 1: 寫 failing test**

```bash
mkdir -p ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/tests
```

建立 `~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/tests/test_ssp_system_implementation_extension.py`：

```python
"""Phase 2 A0: 既有 system_security_plan_system_implementations 表擴充測試"""
from jedi_oscal.common.enum.code_enum import SystemImplementationType


class TestEnumExtension:
    def test_leveraged_authorization_value_exists(self):
        assert SystemImplementationType.LEVERAGED_AUTHORIZATION.value == "leveraged-authorization"

    def test_existing_hardware_value_preserved(self):
        assert SystemImplementationType.HARDWARE.value == "hardware"
```

- [ ] **Step 2: Run test to verify it fails**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestEnumExtension::test_leveraged_authorization_value_exists -v
```

Expected: FAIL with `AttributeError: LEVERAGED_AUTHORIZATION`。

- [ ] **Step 3: 實作 enum 擴充**

編輯 `jedi_oscal/common/enum/code_enum.py`，找到 `class SystemImplementationType`，加最後一行：

```python
LEVERAGED_AUTHORIZATION = "leveraged-authorization"
```

- [ ] **Step 4: Run test to verify it passes**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestEnumExtension -v
```

Expected: 2 passed。

- [ ] **Step 5: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/common/enum/code_enum.py tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): SystemImplementationType 加 LEVERAGED_AUTHORIZATION

Phase 2 A0：支援 OSCAL leveraged-authorization 多型。既有 enum 6 值保留。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 3: SQL Migration（DB Schema + 140 筆資料）

**目的**：DB schema 先擴充才能讓後續 ORM / mapper 整合測試在實際 DB 上跑通。

**Files：**
- Create: `compliance-manager-be/scripts/sql/2026-05-18-extend-ssp-system-implementations.sql`

- [ ] **Step 1: 撰寫 migration SQL**

Create `scripts/sql/2026-05-18-extend-ssp-system-implementations.sql`：

```sql
-- Date: 2026-05-18
-- Purpose: Phase 2 A0 — 擴充 oscal.system_security_plan_system_implementations
--   1. 加 9 個新 column (scope_type / scope_id / device_id / information_system_id /
--      title / purpose / status / party_uuid / date_authorized)
--   2. system_security_plan_id 改 nullable（讓 scope_type='module_frame' 可為 null）
--   3. 既有 140 筆 hardware 資料補 scope_type='ssp' / scope_id
--   4. 加 3 個 index
--   5. Table COMMENT 標示多型用途

BEGIN;

-- 1. ADD COLUMN — 9 個新欄位 (2026-05-18)
ALTER TABLE oscal.system_security_plan_system_implementations
    ADD COLUMN scope_type            varchar(20),
    ADD COLUMN scope_id              integer,
    ADD COLUMN device_id             integer,
    ADD COLUMN information_system_id integer,
    ADD COLUMN title                 varchar(255),
    ADD COLUMN purpose               text,
    ADD COLUMN status                varchar(50),
    ADD COLUMN party_uuid            varchar(36),
    ADD COLUMN date_authorized       date;

-- 2. 既有 140 筆 hardware 資料補 scope_type / scope_id (2026-05-18)
UPDATE oscal.system_security_plan_system_implementations
   SET scope_type = 'ssp',
       scope_id   = system_security_plan_id
 WHERE scope_type IS NULL;

-- 3. scope_type / scope_id 補資料後改 NOT NULL (2026-05-18)
ALTER TABLE oscal.system_security_plan_system_implementations
    ALTER COLUMN scope_type SET NOT NULL,
    ALTER COLUMN scope_id   SET NOT NULL;

-- 4. system_security_plan_id 改 nullable (2026-05-18)
--    既有 CASCADE FK 保留；module_frame scope 時此欄為 null
ALTER TABLE oscal.system_security_plan_system_implementations
    ALTER COLUMN system_security_plan_id DROP NOT NULL;

-- 5. 加 3 個 index 加速 scope / soft FK 查詢 (2026-05-18)
CREATE INDEX IF NOT EXISTS ix_ssp_sys_impl_scope
    ON oscal.system_security_plan_system_implementations (scope_type, scope_id);

CREATE INDEX IF NOT EXISTS ix_ssp_sys_impl_device_id
    ON oscal.system_security_plan_system_implementations (device_id);

CREATE INDEX IF NOT EXISTS ix_ssp_sys_impl_info_system_id
    ON oscal.system_security_plan_system_implementations (information_system_id);

-- 6. Table COMMENT 標示多型 + scope 雙層用途 (2026-05-18)
COMMENT ON TABLE oscal.system_security_plan_system_implementations IS
'OSCAL system-implementation 多型鏡像表（Phase 2 A0 擴充：scope_type 區分 ssp / module_frame）。
 implementation_type 區分 hardware (device 鏡像) / component (information_system 鏡像) /
 leveraged-authorization / system / subsystem / service / software。';

COMMIT;
```

- [ ] **Step 2: 先 dry-run 確認語法**

```bash
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
    -c "BEGIN; \i ~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/2026-05-18-extend-ssp-system-implementations.sql ROLLBACK;" 2>&1 | head -20
```

> 不對，這指令會直接執行整支 SQL（裡頭已有 COMMIT）。實際 dry-run 用：

```bash
# 退而求其次：先檢視 SQL 語法
cat ~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/2026-05-18-extend-ssp-system-implementations.sql
```

確認 SQL 內容無誤。

- [ ] **Step 3: 執行 migration（cmmgr 帳號）**

```bash
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
    -f ~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/2026-05-18-extend-ssp-system-implementations.sql
```

Expected: `BEGIN` / `ALTER TABLE` x3 / `UPDATE 140` / `CREATE INDEX` x3 / `COMMENT` / `COMMIT`。

- [ ] **Step 4: Verify schema + 資料**

```bash
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev <<'EOF'
\d oscal.system_security_plan_system_implementations
SELECT scope_type, COUNT(*) FROM oscal.system_security_plan_system_implementations GROUP BY scope_type;
EOF
```

Expected:
- `\d` 輸出含 scope_type / scope_id / device_id / information_system_id / title / purpose / status / party_uuid / date_authorized 9 個新欄位
- `system_security_plan_id` 行顯示「可空」/「nullable」
- 3 個新 index 列在 indexes 段
- `scope_type='ssp', count=140`

- [ ] **Step 5: Commit (main BE repo)**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git add scripts/sql/2026-05-18-extend-ssp-system-implementations.sql
git commit -m "$(cat <<'EOF'
tweak(ssp-system-impl): 擴充 system_security_plan_system_implementations schema

Phase 2 A0：
- 加 9 個 column (scope_type / scope_id / device_id / info_system_id / title /
  purpose / status / party_uuid / date_authorized)
- system_security_plan_id 改 nullable（讓 module_frame scope 可為 null）
- 既有 140 筆 hardware 補 scope_type='ssp' / scope_id
- 加 3 個 index + Table COMMENT

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 4: jedi-oscal ORM Model 擴充

**目的**：把 §3 SQL 加的 9 欄位反映到 SQLAlchemy ORM；改 `system_security_plan_id` nullable；補 index 宣告。

**Files：**
- Modify: `jedi-oscal/jedi_oscal/infra/model/ssp/ssp_system_implementation.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`

- [ ] **Step 1: 寫 failing test**

在 `tests/test_ssp_system_implementation_extension.py` 加 class：

```python
from jedi_oscal.infra.model.ssp.ssp_system_implementation import (
    OscalSystemSecurityPlanSystemImplementation,
)


class TestOrmExtension:
    def test_new_columns_declared(self):
        cols = OscalSystemSecurityPlanSystemImplementation.__table__.columns
        for name in (
            "scope_type", "scope_id", "device_id", "information_system_id",
            "title", "purpose", "status", "party_uuid", "date_authorized",
        ):
            assert name in cols, f"missing column: {name}"

    def test_system_security_plan_id_is_nullable(self):
        col = OscalSystemSecurityPlanSystemImplementation.__table__.columns["system_security_plan_id"]
        assert col.nullable is True

    def test_scope_columns_not_nullable(self):
        cols = OscalSystemSecurityPlanSystemImplementation.__table__.columns
        assert cols["scope_type"].nullable is False
        assert cols["scope_id"].nullable is False
```

- [ ] **Step 2: Run test to verify it fails**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestOrmExtension -v
```

Expected: FAIL — 9 個欄位都不在 model 上 + nullable 都還是 False。

- [ ] **Step 3: 實作 ORM 擴充**

編輯 `jedi_oscal/infra/model/ssp/ssp_system_implementation.py`。

**(a) imports 段加：**

```python
from datetime import date
from sqlalchemy import Integer, Date
```

（既有 import 已有 `String`、`Text`、`UUID`、`ForeignKey`、`DateTime`、`func`、`Index`，按需要保留）

**(b) `__table_args__` 補 3 個 index（在既有 Index() list 內）：**

```python
__table_args__ = (
    Index("ix_ssp_sys_impl_uid", "uid"),
    Index("ix_ssp_sys_impl_ssp_id", "system_security_plan_id"),
    Index("ix_ssp_sys_impl_ssp_type", "system_security_plan_id", "implementation_type"),
    Index("ix_ssp_sys_impl_scope", "scope_type", "scope_id"),
    Index("ix_ssp_sys_impl_device_id", "device_id"),
    Index("ix_ssp_sys_impl_info_system_id", "information_system_id"),
    {"schema": "oscal", "comment": "..."},
)
```

**(c) 改 `system_security_plan_id` nullable：**

```python
system_security_plan_id: Mapped[Optional[int]] = mapped_column(
    ForeignKey("oscal.system_security_plans.id", ondelete="CASCADE"),
    nullable=True,  # ← 從 False 改成 True
    comment="所屬 SSP ID（scope_type='module_frame' 時為 null）",
)
```

**(d) 在 `responsible_party` 後加 9 個新欄位（含 deprecated 註記給 responsible_party）：**

```python
# ========= Scope (Phase 2 A0) =========

scope_type: Mapped[str] = mapped_column(
    String(20),
    nullable=False,
    comment="'ssp' or 'module_frame' — 此 row 隸屬範圍",
)

scope_id: Mapped[int] = mapped_column(
    Integer,
    nullable=False,
    comment="對應 scope_type 指向的 id（soft FK，無 DB constraint）",
)

# ========= Soft FK to tenant 層 (Phase 2 A0) =========

device_id: Mapped[Optional[int]] = mapped_column(
    Integer,
    nullable=True,
    comment="soft FK → public.devices.id；hardware 鉤稽既有 device 時填",
)

information_system_id: Mapped[Optional[int]] = mapped_column(
    Integer,
    nullable=True,
    comment="soft FK → compliance.information_systems.id；component 鉤稽既有 system 時填",
)

# ========= OSCAL Common Optional Fields (Phase 2 A0) =========

title: Mapped[Optional[str]] = mapped_column(
    String(255),
    nullable=True,
    comment="OSCAL component.title 人類可讀標題（與 name 區分）",
)

purpose: Mapped[Optional[str]] = mapped_column(
    Text,
    nullable=True,
    comment="OSCAL component.purpose 用途說明",
)

status: Mapped[Optional[str]] = mapped_column(
    String(50),
    nullable=True,
    comment="OSCAL component.status (under-development | operational | disposition | other)",
)

# ========= OSCAL Leveraged-Authorization Specific (Phase 2 A0) =========

party_uuid: Mapped[Optional[str]] = mapped_column(
    String(36),
    nullable=True,
    comment="OSCAL leveraged-authorization.party-uuid（授權方；對齊 oscal_responsible_parties.party_uuid 型別）",
)

date_authorized: Mapped[Optional[date]] = mapped_column(
    Date,
    nullable=True,
    comment="OSCAL leveraged-authorization.date-authorized",
)
```

**(e) 在 `responsible_party` 欄位的 docstring 加 deprecated 註記：**

```python
responsible_party: Mapped[Optional[str]] = mapped_column(
    String(100),
    comment="DEPRECATED (Phase 2 A0): 新功能改用 oscal_responsible_parties; 既有資料保留",
)
```

- [ ] **Step 4: Run test to verify it passes**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestOrmExtension -v
```

Expected: 3 passed。

- [ ] **Step 5: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/infra/model/ssp/ssp_system_implementation.py \
        tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): ORM 擴充 — 加 9 欄位 + 改 ssp_id nullable

Phase 2 A0：ORM 反映 SQL schema 擴充。responsible_party 標 deprecated。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 5: jedi-oscal Entity + Query Entity 擴充

**目的**：DDD domain entity 反映 9 個新欄位；Query entity 支援新篩選欄位。

**Files：**
- Modify: `jedi-oscal/jedi_oscal/domain/entity/ssp/ssp_system_implementation_entity.py`
- Modify: `jedi-oscal/jedi_oscal/domain/entity/ssp/ssp_system_implementation_query_entity.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`

- [ ] **Step 1: 確認既有 entity / query entity 結構**

```bash
head -60 ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/entity/ssp/ssp_system_implementation_entity.py
head -50 ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/entity/ssp/ssp_system_implementation_query_entity.py
```

- [ ] **Step 2: 寫 failing test**

加 class 到 `tests/test_ssp_system_implementation_extension.py`：

```python
from datetime import date
from jedi_oscal.domain.entity.ssp.ssp_system_implementation_entity import (
    SspSystemImplementationEntity,
)
from jedi_oscal.domain.entity.ssp.ssp_system_implementation_query_entity import (
    SspSystemImplementationQueryEntity,
)


class TestEntityExtension:
    def test_entity_accepts_new_attributes(self):
        entity = SspSystemImplementationEntity(
            scope_type="module_frame",
            scope_id=1,
            device_id=42,
            information_system_id=None,
            title="Daisy's MacBook",
            purpose="Engineering workstation",
            status="operational",
            party_uuid=None,
            date_authorized=None,
        )
        assert entity.scope_type == "module_frame"
        assert entity.scope_id == 1
        assert entity.device_id == 42
        assert entity.title == "Daisy's MacBook"

    def test_entity_leveraged_authorization_fields(self):
        entity = SspSystemImplementationEntity(
            scope_type="ssp",
            scope_id=10,
            party_uuid="abc-123-def",
            date_authorized=date(2026, 5, 18),
        )
        assert entity.party_uuid == "abc-123-def"
        assert entity.date_authorized == date(2026, 5, 18)


class TestQueryEntityExtension:
    def test_query_entity_accepts_scope_filter(self):
        q = SspSystemImplementationQueryEntity(scope_type="module_frame", scope_id=1)
        assert q.scope_type == "module_frame"
        assert q.scope_id == 1

    def test_query_entity_accepts_soft_fk_filter(self):
        q = SspSystemImplementationQueryEntity(device_id=42, information_system_id=None)
        assert q.device_id == 42
```

- [ ] **Step 3: Run test to verify it fails**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestEntityExtension tests/test_ssp_system_implementation_extension.py::TestQueryEntityExtension -v
```

Expected: FAIL with `TypeError: __init__() got an unexpected keyword argument`.

- [ ] **Step 4: 實作 Entity 擴充**

編輯 `ssp_system_implementation_entity.py`，在 `__init__` 加 9 個參數（mirror Task 4 ORM 欄位）：

```python
def __init__(
    self,
    # ...既有參數...
    scope_type: Optional[str] = None,
    scope_id: Optional[int] = None,
    device_id: Optional[int] = None,
    information_system_id: Optional[int] = None,
    title: Optional[str] = None,
    purpose: Optional[str] = None,
    status: Optional[str] = None,
    party_uuid: Optional[str] = None,
    date_authorized: Optional[date] = None,
):
    # ...既有 self.x = x...
    self.scope_type = scope_type
    self.scope_id = scope_id
    self.device_id = device_id
    self.information_system_id = information_system_id
    self.title = title
    self.purpose = purpose
    self.status = status
    self.party_uuid = party_uuid
    self.date_authorized = date_authorized
```

- [ ] **Step 5: 實作 Query Entity 擴充**

`ssp_system_implementation_query_entity.py` 加同樣 9 個參數（全部 default `None`）。

- [ ] **Step 6: Run test to verify it passes**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestEntityExtension tests/test_ssp_system_implementation_extension.py::TestQueryEntityExtension -v
```

Expected: 4 passed。

- [ ] **Step 7: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/domain/entity/ssp/ssp_system_implementation_entity.py \
        jedi_oscal/domain/entity/ssp/ssp_system_implementation_query_entity.py \
        tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): Entity + QueryEntity 擴充 — 加 9 欄位

Phase 2 A0：Domain layer 反映 ORM 擴充。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 6: jedi-oscal Mapper 擴充（Entity ↔ Model Round-Trip）

**目的**：entity ↔ ORM model 雙向 mapping 包含 9 個新欄位。

**Files：**
- Modify: `jedi-oscal/jedi_oscal/infra/mapper/ssp/system_implementation_mapper.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`

- [ ] **Step 1: 確認既有 mapper 結構**

```bash
cat ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/infra/mapper/ssp/system_implementation_mapper.py
```

- [ ] **Step 2: 寫 failing test**

加 class 到 test 檔：

```python
from datetime import date
from jedi_oscal.infra.mapper.ssp.system_implementation_mapper import (
    SystemImplementationMapper,
)


class TestMapperRoundTrip:
    def test_entity_to_model_includes_new_fields(self):
        entity = SspSystemImplementationEntity(
            name="test",
            implementation_type="hardware",
            scope_type="module_frame",
            scope_id=1,
            device_id=42,
            title="Daisy's MacBook",
            purpose="Engineering workstation",
            status="operational",
        )
        model = SystemImplementationMapper.to_model(entity)
        assert model.scope_type == "module_frame"
        assert model.scope_id == 1
        assert model.device_id == 42
        assert model.title == "Daisy's MacBook"
        assert model.purpose == "Engineering workstation"
        assert model.status == "operational"

    def test_model_to_entity_includes_new_fields(self):
        model = OscalSystemSecurityPlanSystemImplementation(
            name="test",
            implementation_type="leveraged-authorization",
            scope_type="ssp",
            scope_id=10,
            title="AWS GovCloud",
            party_uuid="aws-party-uuid",
            date_authorized=date(2026, 5, 18),
        )
        entity = SystemImplementationMapper.to_entity(model)
        assert entity.scope_type == "ssp"
        assert entity.party_uuid == "aws-party-uuid"
        assert entity.date_authorized == date(2026, 5, 18)
```

- [ ] **Step 3: Run test to verify it fails**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestMapperRoundTrip -v
```

Expected: FAIL with `AttributeError`（mapper 沒 map 新欄位）。

- [ ] **Step 4: 實作 Mapper 擴充**

在 `to_model` 與 `to_entity` 兩個方法內加 9 個新欄位 mapping（pattern 對齊既有欄位）：

```python
@staticmethod
def to_model(entity: SspSystemImplementationEntity) -> OscalSystemSecurityPlanSystemImplementation:
    return OscalSystemSecurityPlanSystemImplementation(
        # ...既有...
        scope_type=entity.scope_type,
        scope_id=entity.scope_id,
        device_id=entity.device_id,
        information_system_id=entity.information_system_id,
        title=entity.title,
        purpose=entity.purpose,
        status=entity.status,
        party_uuid=entity.party_uuid,
        date_authorized=entity.date_authorized,
    )

@staticmethod
def to_entity(model: OscalSystemSecurityPlanSystemImplementation) -> SspSystemImplementationEntity:
    return SspSystemImplementationEntity(
        # ...既有...
        scope_type=model.scope_type,
        scope_id=model.scope_id,
        device_id=model.device_id,
        information_system_id=model.information_system_id,
        title=model.title,
        purpose=model.purpose,
        status=model.status,
        party_uuid=model.party_uuid,
        date_authorized=model.date_authorized,
    )
```

- [ ] **Step 5: Run test to verify it passes**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestMapperRoundTrip -v
```

Expected: 2 passed。

- [ ] **Step 6: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/infra/mapper/ssp/system_implementation_mapper.py \
        tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): Mapper 擴充 — 9 欄位雙向 mapping

Phase 2 A0：entity ↔ ORM round-trip。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 7: jedi-oscal Repository 擴充（3 個新 Query Method）

**目的**：domain repo interface + infra impl 加 3 個 query method（`list_by_scope` / `find_by_device_id` / `find_by_information_system_id`）。

**Files：**
- Modify: `jedi-oscal/jedi_oscal/domain/repository/ssp/system_implementation_repo.py`
- Modify: `jedi-oscal/jedi_oscal/infra/repository/ssp/ssp_system_implementation_repo_impl.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`

- [ ] **Step 1: 確認既有 repo 結構**

```bash
cat ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/repository/ssp/system_implementation_repo.py
cat ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/infra/repository/ssp/ssp_system_implementation_repo_impl.py
```

- [ ] **Step 2: 寫 failing test（用 in-memory SQLite 或 mock — 視既有測試風格）**

> 看既有 `tests/test_ssp_template_module_frame_id.py` 用什麼 DB fixture 對齊。

> **替代方案**：若 jedi-oscal 沒 DB fixture，這個 task 的 query method 在主專案的 integration test (Task 11) 統一驗證，jedi-oscal 端只測 method 簽章存在：

```python
class TestRepoExtension:
    def test_repo_has_list_by_scope_method(self):
        from jedi_oscal.infra.repository.ssp.ssp_system_implementation_repo_impl import (
            SspSystemImplementationRepoImpl,
        )
        assert hasattr(SspSystemImplementationRepoImpl, "list_by_scope")

    def test_repo_has_find_by_device_id_method(self):
        from jedi_oscal.infra.repository.ssp.ssp_system_implementation_repo_impl import (
            SspSystemImplementationRepoImpl,
        )
        assert hasattr(SspSystemImplementationRepoImpl, "find_by_device_id")

    def test_repo_has_find_by_information_system_id_method(self):
        from jedi_oscal.infra.repository.ssp.ssp_system_implementation_repo_impl import (
            SspSystemImplementationRepoImpl,
        )
        assert hasattr(SspSystemImplementationRepoImpl, "find_by_information_system_id")
```

- [ ] **Step 3: Run test to verify it fails**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestRepoExtension -v
```

Expected: FAIL（method 不存在）。

- [ ] **Step 4: 實作 domain repo interface**

`domain/repository/ssp/system_implementation_repo.py`：

```python
from abc import abstractmethod
from typing import List, Optional


class SystemImplementationRepo:
    # ...既有 abstract method...

    @abstractmethod
    def list_by_scope(
        self, scope_type: str, scope_id: int
    ) -> List[SspSystemImplementationEntity]:
        """依 scope_type + scope_id 查全部 row。"""

    @abstractmethod
    def find_by_device_id(
        self, device_id: int
    ) -> List[SspSystemImplementationEntity]:
        """依 device soft FK 反查。"""

    @abstractmethod
    def find_by_information_system_id(
        self, information_system_id: int
    ) -> List[SspSystemImplementationEntity]:
        """依 information_system soft FK 反查。"""
```

- [ ] **Step 5: 實作 infra repo impl**

`infra/repository/ssp/ssp_system_implementation_repo_impl.py`：

```python
def list_by_scope(self, scope_type: str, scope_id: int):
    rows = (
        self.session.query(OscalSystemSecurityPlanSystemImplementation)
        .filter_by(scope_type=scope_type, scope_id=scope_id)
        .all()
    )
    return [SystemImplementationMapper.to_entity(r) for r in rows]

def find_by_device_id(self, device_id: int):
    rows = (
        self.session.query(OscalSystemSecurityPlanSystemImplementation)
        .filter_by(device_id=device_id)
        .all()
    )
    return [SystemImplementationMapper.to_entity(r) for r in rows]

def find_by_information_system_id(self, information_system_id: int):
    rows = (
        self.session.query(OscalSystemSecurityPlanSystemImplementation)
        .filter_by(information_system_id=information_system_id)
        .all()
    )
    return [SystemImplementationMapper.to_entity(r) for r in rows]
```

> 注意 session 用 `self.session` lazy property（對齊 CLAUDE.md「Repo 層 session 必須 lazy property」）。

- [ ] **Step 6: Run test to verify it passes**

```bash
poetry run pytest tests/test_ssp_system_implementation_extension.py::TestRepoExtension -v
```

Expected: 3 passed。

- [ ] **Step 7: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/domain/repository/ssp/system_implementation_repo.py \
        jedi_oscal/infra/repository/ssp/ssp_system_implementation_repo_impl.py \
        tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): Repository 加 3 個 query method

Phase 2 A0：list_by_scope / find_by_device_id / find_by_information_system_id

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 8: jedi-oscal DTO 擴充

**目的**：app layer DTO 反映 9 個新欄位（給 BE app service 用）。

**Files：**
- Modify: `jedi-oscal/jedi_oscal/app/dto/ssp/ssp_system_implementation_dto.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`

- [ ] **Step 1: 確認既有 DTO 結構**

```bash
cat ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/app/dto/ssp/ssp_system_implementation_dto.py
```

- [ ] **Step 2: 寫 failing test**

```python
class TestDtoExtension:
    def test_dto_accepts_new_fields(self):
        from jedi_oscal.app.dto.ssp.ssp_system_implementation_dto import (
            SspSystemImplementationDto,
        )
        dto = SspSystemImplementationDto(
            name="test",
            implementation_type="hardware",
            scope_type="ssp",
            scope_id=1,
            device_id=42,
            title="t",
            purpose="p",
            status="operational",
        )
        assert dto.scope_type == "ssp"
        assert dto.device_id == 42
```

- [ ] **Step 3: Run test fails → 實作 DTO 擴充 → run test passes**

把 9 個欄位加到 DTO（pattern 對齊既有欄位，注意是 dataclass / pydantic / 自訂 class，視既有 DTO 風格而定）。

- [ ] **Step 4: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/app/dto/ssp/ssp_system_implementation_dto.py \
        tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): DTO 擴充 — 9 欄位

Phase 2 A0：app layer DTO 反映 ORM 擴充。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 9: jedi-oscal YAML Mapper（OSCAL 序列化）三分支擴充

**目的**：依 `implementation_type` 把 row 翻譯成正確的 OSCAL JSON 區段：
- `hardware` → `system-implementation.inventory-items[*]`
- `component` / `software` / `service` / `system` / `subsystem` → `system-implementation.components[*]`
- `leveraged-authorization` → `system-implementation.leveraged-authorizations[*]`

**Files：**
- Modify: `jedi-oscal/jedi_oscal/infra/mapper/ssp/ssp_yaml_mapper.py`
- Test: `jedi-oscal/tests/test_ssp_system_implementation_extension.py`

- [ ] **Step 1: 確認既有 yaml mapper 結構**

```bash
cat ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/infra/mapper/ssp/ssp_yaml_mapper.py
```

> 預期看到 yaml mapper 已組 SSP dict 結構，含 system-implementation 段。

- [ ] **Step 2: 寫 failing test**

```python
class TestYamlMapperSerialize:
    def test_hardware_row_becomes_inventory_item(self):
        entity = SspSystemImplementationEntity(
            uid=uuid.UUID("aaaa-1111-..."),
            name="Daisy's MacBook",
            description="Engineering workstation",
            implementation_type="hardware",
            scope_type="ssp",
            scope_id=10,
            device_id=42,
        )
        oscal_dict = SspYamlMapper.serialize_system_implementation_item(entity)
        assert "uuid" in oscal_dict
        assert oscal_dict["description"] == "Engineering workstation"
        # hardware 對 OSCAL inventory-item structure

    def test_component_row_becomes_component(self):
        entity = SspSystemImplementationEntity(
            name="HR System",
            implementation_type="component",
            scope_type="ssp",
            scope_id=10,
            information_system_id=7,
            title="HR System",
            purpose="Personnel management",
            status="operational",
        )
        oscal_dict = SspYamlMapper.serialize_system_implementation_item(entity)
        assert oscal_dict["title"] == "HR System"
        assert oscal_dict["status"] == "operational"

    def test_leveraged_authorization_row_becomes_leveraged_auth(self):
        entity = SspSystemImplementationEntity(
            name="AWS GovCloud",
            implementation_type="leveraged-authorization",
            scope_type="ssp",
            scope_id=10,
            title="AWS GovCloud (FedRAMP High)",
            party_uuid="aws-party-uuid",
            date_authorized=date(2026, 1, 1),
        )
        oscal_dict = SspYamlMapper.serialize_system_implementation_item(entity)
        assert oscal_dict["title"] == "AWS GovCloud (FedRAMP High)"
        assert oscal_dict["party-uuid"] == "aws-party-uuid"
        assert oscal_dict["date-authorized"] == "2026-01-01"
```

> **注意**：實際 method 名 / 簽章 view 既有 mapper 後對齊。若既有 mapper 是聚合層級（一次序列化整個 SSP），則 test 改成驗證 `serialize_ssp` 輸出的 dict 結構含三段。

- [ ] **Step 3: Run test fails → 實作三分支 → run test passes**

實作骨架：

```python
def serialize_system_implementation_item(entity):
    impl_type = entity.implementation_type
    common = {
        "uuid": str(entity.uid),
        "description": entity.description,
    }
    if impl_type == "hardware":
        # OSCAL inventory-item
        return {**common, "props": _build_props_for_inventory_item(entity)}
    elif impl_type == "leveraged-authorization":
        # OSCAL leveraged-authorization
        return {
            **common,
            "title": entity.title,
            "party-uuid": entity.party_uuid,
            "date-authorized": entity.date_authorized.isoformat() if entity.date_authorized else None,
        }
    else:
        # default: OSCAL component (covers system/subsystem/service/component/software)
        return {
            **common,
            "type": impl_type,
            "title": entity.title or entity.name,
            "purpose": entity.purpose,
            "status": {"state": entity.status} if entity.status else None,
        }
```

> 細節參考 OSCAL SSP schema (https://pages.nist.gov/OSCAL/reference/latest/system-security-plan/json-outline/)。

- [ ] **Step 4: Commit (jedi-oscal repo)**

```bash
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal
git add jedi_oscal/infra/mapper/ssp/ssp_yaml_mapper.py \
        tests/test_ssp_system_implementation_extension.py
git commit -m "$(cat <<'EOF'
feat(ssp-system-impl): YAML mapper 三分支序列化

Phase 2 A0：依 implementation_type 翻譯成 OSCAL inventory-items / components /
leveraged-authorizations 三種結構。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 10: 既有 Caller Regression 驗證

**目的**：確認 schema + ORM 擴充後，既有 caller (`project_device_mapping_service` + `ssp_versioning_service`) 不破。

**Files：**
- Create: `compliance-manager-be/tests/test_ssp_system_implementation_regression.py`

- [ ] **Step 1: grep 既有 caller 找測試點**

```bash
grep -rn "system_security_plan_id\|SystemImplementation\|ssp_system_impl" \
    ~/Projects/Billows/Audit-Manager/compliance-manager-be/tests/ 2>/dev/null | head -20
```

- [ ] **Step 2: 跑既有相關測試**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
poetry run pytest tests/ -k "ssp_versioning or system_implementation or device_mapping" -v
```

Expected: 既有測試全綠（schema 擴充 + nullable 改動不影響既有寫入流程）。

- [ ] **Step 3: 寫 BE smoke regression test**

Create `tests/test_ssp_system_implementation_regression.py`：

```python
"""A0 regression：既有 caller 在 schema 擴充後仍能正常寫入。"""
import pytest


class TestSchemaExtensionRegression:
    """新欄位 nullable + 既有欄位 nullable 化後，既有寫入流程不破。"""

    def test_legacy_hardware_write_still_works(self, client, headers, app):
        """模擬既有 project_device_mapping_service 寫入 hardware row。
        若 ORM 對舊 caller 仍可用，這個測試應該綠。"""
        from jedi_oscal.domain.entity.ssp.ssp_system_implementation_entity import (
            SspSystemImplementationEntity,
        )
        from jedi_oscal.infra.mapper.ssp.system_implementation_mapper import (
            SystemImplementationMapper,
        )

        # 用既有 caller 寫法（不填新欄位）
        entity = SspSystemImplementationEntity(
            name="legacy device",
            description="legacy",
            implementation_type="hardware",
            scope_type="ssp",
            scope_id=1,
            # 不填 device_id / title / purpose 等新欄位 → 全部 default None
        )
        model = SystemImplementationMapper.to_model(entity)
        assert model.scope_type == "ssp"
        assert model.device_id is None
        assert model.title is None
```

> 這是 smoke test — 重點不是新欄位 CRUD（jedi-oscal 端已測），是「沒填新欄位也能 mapping」。

- [ ] **Step 4: 跑 smoke regression test**

```bash
poetry run pytest tests/test_ssp_system_implementation_regression.py -v
```

Expected: PASS。

- [ ] **Step 5: BE smoke boot test**

啟動 BE，確認 schema 擴充後啟動不破：

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
# 若已有 BE 跑著，先 kill -9（memory: "Backend restart 必 kill -9"）
lsof -ti:8000 | xargs kill -9 2>/dev/null
set -a; source .env; set +a
poetry run python main_app.py &
BE_PID=$!
sleep 5
# 看 log
tail -30 log/app.log
# 健康檢查
curl -s http://localhost:8000/healthz | head -5
# 關掉
kill -9 $BE_PID 2>/dev/null
```

Expected: BE 順利啟動、`log/app.log` 無 schema mismatch / migration 相關錯誤。

> **若啟動失敗**：root cause 必查（不 ignore），通常是 ORM 欄位與 DB 不對齊。

- [ ] **Step 6: Commit regression test (main BE repo)**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git add tests/test_ssp_system_implementation_regression.py
git commit -m "$(cat <<'EOF'
tweak(ssp-system-impl): A0 regression smoke test

確認既有 caller 在 schema 擴充後仍能正常寫入（未填新欄位也 OK）。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 11: Changelog + Tracker 更新

**目的**：把 A0 ship 紀錄完整歸檔。

**Files：**
- Create: `compliance-manager-be/docs/changelog/2026-05-18-tweak-ssp-system-impl-extension.md`
- Modify: `compliance-manager-be/docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md`
- Modify: `compliance-manager-be/docs/features/FR-011.2-2605-ssp-import-export-phase2/requirement-understanding.md`

- [ ] **Step 1: 撰寫 changelog（type=tweak）**

Create `docs/changelog/2026-05-18-tweak-ssp-system-impl-extension.md`：

```markdown
---
type: tweak
breaking: false
modules: [oscal, jedi-oscal, system-implementation]
issue: —
commit: <BE commit hash>
---

# Phase 2 A0：擴充 system_security_plan_system_implementations 表

## 變更說明（為什麼）

Phase 2 SSP 匯入匯出需要把「合規資源庫」與「專案 SSP 版本」雙向匯入 / 匯出含 devices / information_systems / leveraged services 等資料。既有 `oscal.system_security_plan_system_implementations` 已是 OSCAL system-implementation 多型鏡像表（`implementation_type` enum + 140 筆 hardware 資料），擴充比新建務實。

## 變更範圍

### DB schema（compliance-manager-be）
- 加 9 個欄位：scope_type / scope_id / device_id / information_system_id / title / purpose / status / party_uuid / date_authorized
- system_security_plan_id 改 nullable（讓 scope_type='module_frame' 可為 null）
- 加 3 個 index（scope / device_id / information_system_id）
- 既有 140 筆 hardware 補 scope_type='ssp' / scope_id

### jedi-oscal 套件
- ORM model + entity + query entity + repo + mapper + DTO + yaml mapper 擴充
- enum 加 `LEVERAGED_AUTHORIZATION = "leveraged-authorization"`
- 新 repo method：list_by_scope / find_by_device_id / find_by_information_system_id

## API 變更
無（A0 是基礎建設，未動 endpoint）。

## 測試結果
- jedi-oscal `tests/test_ssp_system_implementation_extension.py` 全綠
- 主專案 `tests/test_ssp_system_implementation_regression.py` 全綠
- 既有 `project_device_mapping_service` / `ssp_versioning_service` regression 通

## 參考資訊
- design: docs/features/FR-011.2-2605-ssp-import-export-phase2/design.md
- plan: docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A0.md
- tracker: docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md
```

- [ ] **Step 2: 更新 master tracker README.md**

把 A0 row 狀態改為 `shipped`，填 design / plan / ship commit / changelog 連結。

- [ ] **Step 3: 更新 requirement-understanding.md**

在 §6 Track A 階段表的 A0 row 加 `✅ shipped (commit hash, 2026-05-18)` 標記。

- [ ] **Step 4: Commit (main BE repo)**

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git add docs/changelog/2026-05-18-tweak-ssp-system-impl-extension.md \
        docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md \
        docs/features/FR-011.2-2605-ssp-import-export-phase2/requirement-understanding.md
git commit -m "$(cat <<'EOF'
docs(ssp-import-export-phase2): A0 shipped — 更新 changelog + tracker

Phase 2 A0 既有表擴充完成。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

- [ ] **Step 5: 回填 changelog commit hash**

剛 commit 完拿到 hash，回頭把 `2026-05-18-tweak-ssp-system-impl-extension.md` 內 `commit:` 改成實際 hash，再 amend 上一個 commit（或新建 chore commit）。

> 對齊既有慣例：看 git log 內有 `chore(changelog): 補 BE commit hash xxxx 到 ...` 的 follow-up pattern。

---

## Task 12: 提醒 raymond 重啟 BE + 收口

**目的**：A0 完工通知，提醒重啟 BE（CLAUDE.md memory：「改 BE service 後必提醒 user 重啟」）。

**動作（無 step checkbox，是給人類執行的通知）：**

通知 raymond：

> A0 完工。改動範圍：
> - DB schema migration 已執行（dev DB）
> - jedi-oscal 8 個層擴充完成（local commits 在 jedi-oscal repo）
> - 主專案 regression test 通過、BE smoke boot 通過
>
> **請 restart BE 讓改動生效**：
> ```bash
> lsof -ti:8000 | xargs kill -9
> cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
> set -a; source .env; set +a
> nohup poetry run python main_app.py > /dev/null 2>&1 &
> ```
>
> **jedi-oscal 套件目前在 path dependency mode（dev only）**，等 Phase 2 整體完工後再 bump 版本推 Nexus。
>
> 接下來可開工 A1（Excel 樣板）/ B1（Docx 樣板）— 兩條可平行。

---

## Acceptance Criteria（A0 整體驗收，對齊 design.md §10）

- [ ] Migration SQL 在 dev DB 跑通，140 筆既有資料 scope_type='ssp' 補全
- [ ] jedi-oscal 8 個層擴充完成（ORM / entity / query_entity / repo interface + impl / mapper / yaml_mapper / DTO / enum）
- [ ] jedi-oscal 新欄位 CRUD 單元測試 + scope query 測試通過
- [ ] Mapper round-trip 測試通過
- [ ] OSCAL YAML 三分支 serialize 測試通過
- [ ] 既有 caller (`project_device_mapping_service` + `ssp_versioning_service`) integration 測試通過
- [ ] 主專案 dev 啟動 + smoke test 通過
- [ ] Changelog 完成（type=tweak）
- [ ] Master tracker README.md A0 row 更新為 shipped
- [ ] requirement-understanding.md A0 段加 ✅ shipped 標記

---

## 風險與緩解（從 design.md §8 帶入）

| 風險 | 緩解 |
|------|-----|
| 既有 caller regression | Task 10 跑既有測試 + smoke boot |
| 140 筆資料 migration 出錯 | Migration SQL 包 transaction；Task 3 Step 4 SELECT count 對帳 |
| ORM 欄位與 DB 不對齊 → BE boot 失敗 | Task 3 完成才開 Task 4（schema 先擴；ORM 後跟）|
| jedi-oscal 改後既有 method 簽章衝突 | TDD 每 task 先跑既有測試，破了立刻發現 |

---

## 不在 A0 範圍（提醒實作者，避免 scope creep）

- ❌ Excel parser / 匯入流程 → A2
- ❌ 鉤稽演算法 → A3 / A4
- ❌ Confirm 寫入 service（含 inline 新建 device）→ A5
- ❌ docx generator → B1 / B2
- ❌ OSCAL JSON / XML serialize → B5
- ❌ `responsible_party` varchar 資料遷移 → 最後統整優化清單
- ❌ Frontend 改動 → A5 / B6
