本卡屬 FR-090(母卡 CM-1682)第 3 棒。動 app/auth/service/ 六檔、app/user_auth_provider/service/ 一檔、jedi-iam 套件。重量驗證。含套件異動,決策者 2026-09-12 已裁「開 identity port」。第 2 棒(CM-1684)已把兩個 port 實作搬到 infra/iam/,本卡接續。
jedi-iam 抽出來的時候,主專案在 app/auth/ 留了六個檔做三種事:兩支「轉發殼」(套件的服務外面再包一層,只為了塞產品知識)、兩支「薄殼」(只做接線,跟 core/iam_wiring.py 職責重疊)、兩支 wrapper(把帳號換成暱稱顯示,順便做 root admin 保護與 license 守門)。jedi-asset 的做法是這些全部不存在:port 實作放 infra/、接線放 core/、補暱稱套件自己做。本卡把 iam 收成同一種形狀。
首腦核對(2026-09-12,每檔讀過、每個 caller grep 過):
tenant_provisioning_service.py(140 行):TenantProvisioningService 繼承套件同名 class 只為注入三張 port——ProvisioningLicensedModules(license 過濾)、SubTenantQuotaGuard(子租戶額度)、TENANT_ADMIN_EXCLUDED_RESOURCE_TYPES 常數。caller:di_containers/auth/auth_containers.py:260、app/system_config/service/tenant_storage_config_seeder.py、app/setup/service/setup_wizard_service.py、test/test_tenant_provisioning_license_filter.py。user_batch_import_app_service.py(103 行):UserBatchImportAppService 包套件的 UserBatchImportService,帶兩張 port——AccountMessageBuilder(密碼信文案)、_NotifierAdapter(寄信)。caller:di_containers/auth/auth_containers.py:321、scripts/build/i18n_probe.py。login_service.py(54 行):薄殼,process_login 只是 build_login_config + 讀 MFA_REQUIRED + 轉呼叫套件。caller:di_containers/login/login_containers.py、infra/iam/login_policy_provider.py(docstring 引用)、docs/api/auth/generate_docx.py。app/user_auth_provider/service/user_auth_provider_service.py(41 行):同型薄殼。caller 8 處含 core/iam_wiring.py、api/user_auth_provider/routes/、兩個 dashboard DI。user_app_service.py(122 行):get_users_and_pager 呼叫 enrich_audit_nickname_objs 補暱稱(這段要拿掉);assert_root_admin_* 三支+_is_protected(留,產品規則)。caller:di_containers/auth/auth_containers.py:312。role_app_service.py(95 行):get_roles_and_pager 補暱稱(拿掉);_assert_new_capabilities_licensed(留,license 守門)。caller:auth_containers.py:230。user_import_template_app_service.py:檔頭明寫「留主專案」,Excel 範本產品客製。本卡不動。IdentityAdapters(plugin.py:208)欄位有 notifier/settings/otp_message_builder/account_message_builder/services/hooks,沒有 identity。UserService.__init__ 收七個 domain service 無 identity;RoleService.__init__ 收兩個。UserDTO.created_user_name 靠 infra/models/user.py:89 的 association_proxy 帶值,wrapper 檔頭說「未 eager load 常為 None」。主專案:/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/
app/auth/service/tenant_provisioning_service.py 殼刪;三張 port 搬 infra/iam/provisioning_ports.py
app/auth/service/user_batch_import_app_service.py 殼刪;兩張 port 搬 infra/iam/account_message.py
app/auth/service/login_service.py 併進 core/iam_wiring.py
app/user_auth_provider/service/user_auth_provider_service.py 併進 core/iam_wiring.py
app/auth/service/user_app_service.py 拿掉補暱稱;守門三支留,改名 root_admin_guard.py 搬 infra/iam/?(見怎麼修 ⑤)
app/auth/service/role_app_service.py 拿掉補暱稱;license 守門留(同上)
di_containers/auth/auth_containers.py 改 provider
di_containers/login/login_containers.py 改 provider
core/iam_wiring.py 接收薄殼邏輯、填 identity
common/util/audit_nickname.py 第 4 棒動,本卡不碰
套件:/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-iam/
jedi_iam/plugin.py:208 IdentityAdapters 加 identity 欄位
jedi_iam/app/service/user_service.py:41 __init__ 加 identity=None;四支 get_* 回傳前補暱稱
jedi_iam/app/service/role_service.py:17 同上,三支 get_*
jedi_iam/app/service/_audit_names.py 新建,照抄 jedi-asset 那支
jedi_iam/infra/models/user.py:89 association_proxy 那行:見 ⑥
tests/ 補 identity 注入的 case
IdentityAdapters 加 identity: Optional[Any] = None,docstring 一行「D8 標準件 jedi_common.identity.IdentityContext,補審計暱稱用;缺了回 None」。jedi_iam/app/service/_audit_names.py,逐字照抄 jedi-asset/jedi_asset/app/service/_audit_names.py(fill_user_names(dtos, identity))。先查 jedi-common 有沒有升格成共用件;沒有就抄,commit message 註明「兩份相同,待 jedi-common 收」。UserService.__init__ 加 identity=None;get_users_and_pager/get_users/get_user/get_user_by_uid 回傳前 fill_user_names(...)。RoleService 同型:get_roles_and_pager/get_roles/get_role。plugin.py 的 build_services()(若有)或 DI 組裝點把 adapters.identity 傳進兩支 service。先查:jedi-iam 的 plugin 有沒有 build_services;沒有就照 jedi-asset plugin/assembly.py 補一支最小的。