本卡屬 FR-091(母卡 CM-1690)三張大卡之一。重:39 條 route、ctx 57 處、38 個 entity 全非 dataclass、17 張表、註解 139 處,是套件裡最大的一支,而且它是身分與授權的疆界——登入、JWT、角色、租戶、MFA 全在這裡,改錯任何預設值整站登不進去。主專案接線已在
core/plugins/identity.py(CM-1689)與core/plugins/participant.py,本卡不動主專案。identity port 已於 CM-1686 開好(plugin.py:237 identity+app/service/_audit_names.py),本卡不重做。建議拆成 A~F 六個 commit 之外,D 組(38 個 entity)再按目錄分兩三個 commit,每個 commit 都跑一次 457 支測試。
jedi-iam 是抽出時最早、最大的一支,形狀是舊的:plugin.py 463 行塞 7 個 dataclass 加四張 Protocol port(INotifier/ISettingsReader/IOtpMessageBuilder/IAccountMessageBuilder)加 register 本體;api/init.py 229 行裝守門與 ctx() 與整張路由表;logger 五種名(**name** 22 處、common.jedi_iam 9、"api" 3、"infra"、"domain" 各 1)——後三種與主專案撞名,22 處 **name** 落不到 app.log;38 個 entity 全手寫 **init**;139 處施工日誌(CM-14xx/P3/P8 沿革)。整成 jedi-asset 那個樣子。
首腦核對(2026-09-13):plugin.py 463 行 7 dataclass+4 Protocol;api/init.py 229 行;route 39;ctx() 57;entity 檔 38(含 mfa/domain/entity/,0 dataclass);表 17;migration 0(表由主專案建);tests 457 支全綠(baseline 已跑:457 passed);harness 有 compose、無 --smoke(main 直接建表起服務);施工日誌 139 處;logger 五種;identity 已開(CM-1686);tests/.../PKG_ROOT = parents[1] / "jedi_iam"(不中 plugin 坑,不用改)。主專案 59 檔 import;venv 指工作樹。PLUGINS 順序紅線:identity 必須早於 file_upload(core/plugins/init.py docstring),本卡不動順序。
套件:/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-iam/
jedi_iam/plugin.py(463 行;四張 Protocol port 在 56-117 行)
jedi_iam/api/__init__.py(229 行)
jedi_iam/domain/entities/ 與 jedi_iam/mfa/domain/entity/(38 個 entity 檔)
範例:jedi-asset/;Protocol port 搬 domain/ports.py 的做法看 jedi-flow-engine CM-1714 commit fd1dac9
SOP:extraction-sop.md §4.3/§3.4/§8;說明:plugin-anatomy.md
主專案 import 面:grep -rn "from jedi_iam\|import jedi_iam" compliance-manager-be --include='*.py' --exclude-dir=.venv(59 檔,含 common/authz/ 五支 re-export shim——那些 import 面是 FR-069 P8 契約,一字不能斷)
主專案接線(不動):core/plugins/identity.py、core/plugins/participant.py
common/authz 相關的「為什麼 shim 要留」「字串值凍結」屬陷阱留一到兩行。[plugin.py](<http://plugin.py>) → plugin/ 五檔(無 migration 不建 migrations.py);四張 Protocol port → domain/[ports.py](<http://ports.py>);api/**init**.py → [guards.py](<http://guards.py>)(runtime()+lazy decorator)+[routing.py](<http://routing.py>)(39 條)。對外 import 一字不改——from jedi_iam.plugin import X、from jedi_iam.authz import Y(主專案 common/authz shim 靠它)、from jedi_iam.api import Z,59 檔用 AST 掃逐一實跑。Blueprint(name, "jedi_iam.plugin") 寫死;EXTENSION_KEY 字串 "iam" 不改。common.jedi_iam(改完跑主專案 test/test_jedi_package_logger_names.py);命名統一。inspect.signature 逐欄對照預設值,比對表貼進回寫。特別盯:User 的 is_active/is_admin/status、Role 的權限預設、Tenant 的 path、MFA 的 enabled——這些預設值錯了是登不進去或權限放大。查詢 entity 有 _in_xxx 底線欄位的照 flow-engine 做法保留。--smoke(test client 走一次 login → 帶 token 打 users/menu 200 → 不帶 401 → 非 admin 打 admin 端點 403);pyproject.toml 加 addopts = "--import-mode=importlib";README 補新結構。tests/integration/test_service_behaviour.py(目錄新建,17 張表用 Base.metadata.create_all,conftest 照 jedi-system-core 自建最小生態):建租戶→建使用者→指派角色→登入取 token→查 users/menu 一輪;RLS 下跨租戶讀不到。test_plugin_contract.py 凍結集合(39 URL、error code 字串 GRC_*)整理前後同綠。.venv/bin/python -c "import jedi_iam;print(jedi_iam.**file**)"。pytest tests/ --import-mode=importlib(需 docker);harness --smoke;主專案 pytest test/test_module_boundaries.py test/test_jedi_package_logger_names.py + test/test_fr048_*.py 全部;主專案 59 檔 import AST 掃逐一實跑。