---
title: 套件分類地圖 — 每支套件是哪一族
brand: Guidant AI · **架構手冊**
eyebrow: 架構手冊 · 套件分類 · living
h1: 二十支套件，十個族——誰跟誰是同一類
lede: 這頁回答「**這支套件屬於哪一類、為什麼跟那支不是同一支**」。分類是給**判斷**用的——新能力要抽套件時照這張表找族群、找同族的先例照抄；但**同一族不代表可以合併**，那是兩件事（見「同類型≠同疆界」）。決策全文在 [FR-069 站的 design.md](../FR-069-2608-jedi-module-extraction/design.md) §3 決策表。
chips: [{text: 十族分類, kind: accent}, {text: 骨幹不抽, kind: warn}, {text: bundle 未做, kind: plain}]
---

## 為什麼要分類 {#why nav="為什麼"}

套件到了二十支，光看名字已經看不出關係——`jedi-iam`、`jedi-integrity`、`jedi-oscal-v2` 擺在一起，像是同一層的三個東西，實際上一個是「每個產品都要的身分底座」、一個是「只有落地版才裝的營運元件」、一個是「產品核心價值本身」。三者的**發版節奏、誰該裝、壞掉的後果**完全不同。

分類要回答的是三個實際問題：

::: grid2
::: {.card .ok}
#### ① 新能力該抽成什麼

要抽一支新套件時，先找它屬於哪一族，照該族既有的先例抄——不必每次重新想「這個要不要自帶 api 層」「要不要 port 化」。
:::
::: {.card .ok}
#### ② 新產品要裝哪幾支

不是每個產品都要裝二十支。分族之後可以說「新產品至少要身分族＋基座」「落地版才要加落地營運服務族」。
:::
:::

::: grid2
::: {.card .warn}
#### ③ 這支壞了會怎樣

同族的套件壞掉的後果相近——基座壞了全停、落地營運服務壞了可降級繼續跑。這決定值不值得為它做高可用。
:::
::: {.card .crit}
#### ④ 分類**不**回答的問題

「這兩支要不要合併」——那是疆界問題不是分類問題，判準完全不同（見下節）。
:::
:::

## 🔴 同類型 ≠ 同疆界 {#not-same nav="類型≠疆界"}

這是本頁最容易被誤用的一點，先講清楚。

::: {.callout .crit}
**分類看「長得像不像」，疆界看「是不是同一件事」——兩者無關**

`jedi-integrity`（防篡改）與 `jedi-license-runtime`（授權驗證）同屬**落地營運服務族**：都只有落地版會裝、都在啟動時把關、壞了都會擋住服務。但它們**不是同一個疆界**——防篡改管「這包程式有沒有被改過」、授權管「這個客戶有沒有買」，兩件事各自獨立演化，合併只會得到一支「安裝時做很多事」的雜物袋。

反過來也成立：問卷設計（`jedi-survey`）與作答層（主專案 task_survey）**分屬不同層**（一個是套件、一個是主專案模組），看起來完全不同類，卻是**同一個疆界**——兩者之間有跨邊界外鍵、資料同生共死，因此裁定合併（D10）。
:::

判準各自是：

| 問題 | 用什麼判 | 依據 |
|------|----------|------|
| **同不同族**（本頁） | 誰會裝它、壞了什麼後果、發版節奏跟誰綁 | 本頁分類表 |
| **同不同疆界**（該不該合併） | 要不要天天連表查、要不要同生共死——**想建外鍵就是同一個疆界** | design.md D17 資料關聯三律 ① |

一句收攏：**同族是鄰居，同疆界才是一家人。鄰居再像也不併戶口。**

## 十族分類表 {#families nav="十族"}

```{.mermaid cap="圖 1 — 十族的層次關係：骨幹在最底不抽、基座之上是各功能族、落地營運服務只有落地版裝"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart TB
  subgraph L3["每個產品都可選裝"]
    A["① 身分族<br/>jedi-iam"]
    B["② 業務平台服務<br/>通知／檔案／公告／設定／選單"]
    C["③ 落地營運服務<br/>log-forwarding／license-runtime／integrity"]
  end
  subgraph L2["本產品的價值所在"]
    D["④ 領域核心<br/>OSCAL／module_frame"]
    E["⑤ 流程族<br/>flow-engine／flow_control"]
    F["⑥ 評估執行族<br/>detection／evidence-classification／問卷"]
    G["⑦ 人員指派<br/>participant"]
  end
  subgraph L1["連到外面世界"]
    H["⑧ Edge<br/>remote-agent／cloud_integration"]
  end
  I["⑨ 基座　jedi-common<br/>（唯一允許被直接依賴）"]
  J["⑩ 骨幹　主專案<br/>不抽——聚合層／接線盤／輪次編排"]
  L3 --> I
  L2 --> I
  L1 --> I
  J -. 接線注入 .-> L3
  J -. 接線注入 .-> L2
  J -. 接線注入 .-> L1
```

### ① 身分族 {#f1}

| | |
|---|---|
| **成員** | `jedi-iam`（auth＋login＋mfa＋captcha 四合一，D9） |
| **誰會裝** | **每個產品都要**——沒有產品不需要登入 |
| **壞了會怎樣** | 全停。沒人進得來 |
| **形態** | 插件（完全體，2.5 階段已達成） |
| **辨識句** | 「你是誰、你能不能進來、你能做什麼」 |

身分是唯一一個「所有產品必裝、且不可能自己不做」的族。四支合併成一支的理由（D9）：它們從未獨立演化過——近三次發版五支全部同批 bump。

### ② 業務平台服務族 {#f2}

| | |
|---|---|
| **成員** | `jedi-notification`、`jedi-file-upload`、`jedi-bulletin`、`jedi-system-config`、`jedi-system-menu`、`jedi-information-system`、`jedi-issue`、`jedi-device`、`jedi-log`（存活半） |
| **誰會裝** | 選裝——大部分產品會裝其中幾支 |
| **壞了會怎樣** | 該功能不能用，其他照跑（可降級） |
| **形態** | 插件（第三階段已補殼完畢） |
| **辨識句** | 「每個產品都會重寫一次的通用功能」 |

這族最大，也最符合「抽套件」的直覺定義：**跟業務無關、換個產品照樣用**。`jedi-file-upload` 是全庫唯一有第二消費者的套件（`jedi-issue` 在用）。

### ③ 落地營運服務族 {#f3}

| | |
|---|---|
| **成員** | `jedi-log-forwarding`、`jedi-license-runtime`、`jedi-integrity` |
| **誰會裝** | **接授權體系的產品**（見下方判準） |
| **壞了會怎樣** | 視政策——integrity／license 壞了會**主動擋住服務**（那是它們的工作）；log-forwarding 壞了可降級 |
| **形態** | 插件 |
| **辨識句** | 「客戶把產品裝進自己機房之後，我們還需要管的事」 |

::: {.callout .warn}
**🔴 判準是「接不接授權體系」，不是「落地不落地」**

族名叫「落地營運」是因為這三支都是為落地版（客戶自建機房）長出來的，但**雲端版同樣適用**——雲端 SaaS 一樣要驗授權（哪個租戶買了哪些模組）、一樣要轉發稽核 log 到客戶的 SIEM、一樣可能要驗程式包完整性。

**正確的判準是：這個產品要不要接入授權／完整性體系？** 要，就裝這族；不要（例如純內部工具），就不裝。用「落地與否」判會漏掉雲端版的授權需求。
:::

### ④ 領域核心族 {#f4}

| | |
|---|---|
| **成員** | `jedi-oscal-v2` 系、module_frame（第四階段 4.3 定形） |
| **誰會裝** | 合規類產品才裝 |
| **壞了會怎樣** | 產品失去核心價值——控制項／框架都讀不到 |
| **形態** | 插件 |
| **辨識句** | 「這是合規產品之所以是合規產品的東西」 |

這族是**產品價值本身**，不是通用能力。抽它的動機不是「別人也要用」，而是「合規知識該有自己的家、不要散在四十個模組裡」。

### ⑤ 流程族 {#f5}

| | |
|---|---|
| **成員** | `jedi-flow-engine`（引擎，推流程）、flow_control（控管，掛業務——第四階段 4.2 拆出） |
| **誰會裝** | 有流程需求的產品 |
| **壞了會怎樣** | 任務推不動，但既有資料還在 |
| **形態** | 插件（切分待第二種流程出現時再議） |
| **辨識句** | 「引擎推流程、控管掛業務」 |

兩支成對命名不是巧合（D5）：引擎管「下一步是誰」，控管管「這一步要做什麼業務」。稽核只是第一個掛進來的業務，未來問卷審核等都會套同一模式。

### ⑥ 評估執行族 {#f6}

| | |
|---|---|
| **成員** | `jedi-detection`（4.4）、`jedi-evidence-classification`（4.5）、問卷疆界套件（4.6，D10 合併產物） |
| **誰會裝** | 要「實際去查證」的產品 |
| **壞了會怎樣** | 該種查證方式不能用，其他查證照跑 |
| **形態** | detection **開工前先過服務化評估**（D7-③）；其餘插件 |
| **辨識句** | 「三種問法：機器掃、看文件、問人」 |

這族是同一件事的三種手段——要知道一個控制項到底有沒有落實，可以**機器掃**（detection）、**看證據**（evidence-classification）、**問人**（問卷）。三支同族但**各自獨立疆界**（同類型≠同疆界的典型）。

### ⑦ 人員指派族 {#f7}

| | |
|---|---|
| **成員** | `jedi-participant`（4.1，核心抽取首棒） |
| **誰會裝** | 要「把事指派給人」的產品 |
| **壞了會怎樣** | 任務沒有負責人 |
| **形態** | 插件 |
| **辨識句** | 「誰負責這件事」 |

單獨成族因為它**誰都不像**——不是身分（身分管「你是誰」，它管「這件事歸你」）、不是流程（流程管「下一步」，它管「這一步歸誰」）。全專案最大 hub（被 60+ 處依賴）但 out-degree 僅 8，是解核心環的鑰匙。

### ⑧ Edge 族 {#f8}

| | |
|---|---|
| **成員** | `jedi-remote-agent`、cloud_integration（Drive 整合，待定序） |
| **誰會裝** | 需要「伸手到系統外面」的產品 |
| **壞了會怎樣** | 外部連線斷，本體照跑（可降級） |
| **形態** | 插件（remote-agent 已發版，migration 隨包首例） |
| **辨識句** | 「跨出這台機器去拿東西／派工」 |

::: {.callout .decided}
**機器身分不併身分族**

`jedi-remote-agent` 管的是**機器**的身分（mTLS／X-Agent-Uid），`jedi-iam` 管的是**人**的身分。兩套認證體系平行、明確不併（D9）——機器不會忘記密碼，人不會用憑證輪替。
:::

### ⑨ 基座 {#f9}

| | |
|---|---|
| **成員** | `jedi-common`（層 0） |
| **誰會裝** | **全部**——它是所有套件的地基 |
| **壞了會怎樣** | 全停 |
| **形態** | 函式庫（不是插件——它沒有 api 層也不該有） |
| **辨識句** | 「session、查詢基底、例外類、身分脈絡包」 |

::: {.callout .warn}
**唯一允許被直接依賴的套件**

架構只有一條實線規則：**疆界插件只准直接依賴 jedi-common，插件彼此絕不直接相依**（D16）。插件間協作一律經宿主接線盤走 port 注入。jedi-common 是這條規則的唯一例外。
:::

### ⑩ 骨幹（不抽） {#f10}

| | |
|---|---|
| **成員** | 主專案本體——跨疆界聚合查詢、DI 接線盤、輪次生命週期編排、associations 關聯表 |
| **誰會裝** | 不適用——它是宿主本身 |
| **壞了會怎樣** | 全停 |
| **形態** | **永不成為套件** |
| **辨識句** | 「把各檔口的菜湊成套餐的那個櫃檯」 |

::: {.callout .crit}
**🔴 這族存在的意義是「明文標記不可搬」**

第四階段最可能出事的地方，是把骨幹的東西誤判成某支套件的資產一起搬走——尤其**跨疆界聚合查詢**：它們現在散在 `infra/oscal/`、`infra/flow_control/`、`infra/detection_tools/` 各模組底下，長得跟該模組的 repository 一模一樣，但 SQL 內部 JOIN 了七個疆界的表。**grep 守衛與 standalone harness 都抓不到 SQL 字串裡的跨表依賴**——誤搬進套件不會有任何測試變紅，會在客戶端執行時才炸。

骨幹的四類內容：①跨疆界聚合查詢（櫃檯的套餐窗口）②DI 接線盤（哪支 port 接哪個實作）③輪次生命週期編排（產品核心價值，本來就該在宿主）④associations 關聯表（隨「擁有語意的那端」走，過程中逐步溶解）。
:::

## 分族速查：新能力要抽套件時怎麼找 {#howto nav="速查"}

```{.mermaid cap="圖 2 — 新能力的分族判斷樹：先問誰會裝，再問壞了會怎樣"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart TB
  A{"這個能力<br/>換個產品還要嗎？"} -->|不要，只有合規產品要| B{"是產品核心知識<br/>還是查證手段？"}
  B -->|核心知識| C["④ 領域核心"]
  B -->|查證手段| D["⑥ 評估執行"]
  A -->|要| E{"沒有它<br/>產品就進不去嗎？"}
  E -->|是| F["① 身分族"]
  E -->|否| G{"是給客戶自建機房<br/>／授權體系用的嗎？"}
  G -->|是| H["③ 落地營運服務"]
  G -->|否| I{"要連到<br/>系統外面嗎？"}
  I -->|是| J["⑧ Edge"]
  I -->|否| K["② 業務平台服務"]
  A -->|它只是把別人的資料湊起來| L["⑩ 骨幹——不抽"]
```

找到族之後，照該族既有的先例抄——每一族都已經有至少一支做完的樣板。

## bundle（成套安裝）——構想，未做 {#bundle nav="bundle"}

::: {.callout .pending}
**現況：只是一個構想，本階段不實作**

分完族之後很自然的下一步是：**新產品不要一支支列依賴，直接裝一包**。構想中兩個 meta-package（本身沒有程式碼，只宣告依賴清單）：

| 名稱 | 內含 | 給誰 |
|------|------|------|
| `jedi-platform-suite` | 身分族＋業務平台服務族＋基座 | 任何新產品的起手式 |
| `jedi-onprem-suite` | 落地營運服務族（log-forwarding／license-runtime／integrity） | 要接授權體系的產品加裝 |

**為什麼現在不做**：meta-package 的價值是「省下列依賴的力氣」，但代價是**多一層版本對版的維護**——每支套件發版都要回頭決定 bundle 要不要跟著 bump。在只有一個消費產品的現在，這個代價大於收益。

**什麼時候回來做**：出現第二個實際要裝的產品時。屆時「新產品該裝哪幾支」會從紙上談兵變成真實問題，bundle 的內容也才有依據定。
:::

## 這頁怎麼維護 {#maintain nav="維護"}

- **每支套件的即時狀態**（已發版／待補殼／退役中）看 design.md §8.3 逐套件狀態表——**那裡是維護主體**，本頁只記分族不記狀態，避免兩處同一事實漂移。
- **分族有變動時才改這頁**：新抽一支套件歸入某族、或某支套件改族（罕見）。
- 第四階段各棒收口後，若該棒的成果推翻了某族的判準（例如 detection 走服務化而非插件），回頭改對應族的「形態」欄並在此註記。
