本卡屬 FR-090(母卡 CM-1682)第 6 棒 CM-1696 拆出的第 2 子卡,主專案側。前置:第 6-1 棒(套件側取用口)已 commit,且 BE 走 path dependency 吃到那版。 純主專案,不動套件。
主專案的 DI container(依賴注入容器,「誰負責建哪個 service」的組裝表)現在替每支套件把它自己的 repo → domain service → app service 整條再建一遍——bulletin 97 行、iam 352 行、license 84 行、asset 89 行。但套件自己的 build_services() 本來就會自組,這層重複是「接一支套件要動第四個檔」的原因。這一棒把重複拆掉:零引用的 container 整個刪;有引用的只留「主專案 adapter 需要的 provider」;48 處直接伸手進套件 container 的寫法,全部改成從套件的取用口 host_services(app) 拿。
首腦核對(2026-09-13,BE HEAD 3bf476a9):
di_containers/{bulletin,issue,license,asset}/(refs_outside=0)。iam(di_containers/auth/)refs_outside=34,不可整刪——卡上 2026-09-12 寫「iam 0」是以套件 container 名 grep 的結果,實際主專案叫它 auth_container;本卡只瘦不刪它。task_platform 沒有獨立 container 目錄。di_containers/containers.py:178 detection_tools_container.upload_file_container.override(upload_file_container) 是 DependenciesContainer 覆寫,改法不同於 Provide——detection_tools 的兩個 container 要改成直接收 provider 參數。core/plugins/<pkg>.py 的 build_adapters() 目前從 container 拿 services=(如 core/plugins/file_upload.py:94-97)。改成不填 services=讓套件自組;但第 6-1 棒回寫標記為「宿主必填」的 provider 仍由這裡傳(那些要保留 container provider)。di_containers/containers.py 組裝時套件尚未 register()。時序:core/app_factory.py 先 wire container、再 mount plugins。所以主專案 consumer container 收的不能是「現在就解析」的值,要收 lazy provider——用 providers.Callable(lambda: host_services(app).file_upload_service()) 或等價的延遲取法,runner 先在 core/app_factory.py 確認 mount 順序再定寫法,並把定法回寫本卡。~/Projects/Billows/Audit-Manager/compliance-manager-be/ branch feature/FR-075
整刪(zero refs):di_containers/bulletin/ di_containers/issue/ di_containers/license/ di_containers/asset/ + containers.py 對應宣告
瘦:di_containers/upload_file/ system_core/ survey/ notification/ auth/(iam,只瘦 34 處引用不到的 provider)
48 處改走取用口:
di_containers/containers.py:178 detection_tools override
di_containers/flow_control/flow_control_containers.py:191,222 file_upload_service ×2
di_containers/flow_control/flow_control_containers.py:264 notification_service
di_containers/flow_control/flow_control_containers.py:414 survey_snapshot_domain_service
di_containers/notify_config/containers.py:12 system_config_service
di_containers/auth/auth_containers.py:288 tenant_storage_config_seeder
di_containers/setup/setup_containers.py:41 tenant_storage_config_seeder
di_containers/cloud_integration/cloud_integration_containers.py:206 file_upload_service
di_containers/feedback/feedback_containers.py:50,63 system_config_service ×2
di_containers/feedback/feedback_containers.py:56 file_upload_service
di_containers/task_survey/task_survey_containers.py:42 system_config_service
di_containers/task_survey/task_survey_containers.py:48,49 survey_domain_service / survey_question_domain_service
di_containers/oscal/oscal_containers.py:237,359,454,493 file_upload_service ×4
di_containers/oscal/oscal_containers.py:358,387 upload_file_domain_service ×2
di_containers/module_frame/module_frame_containers.py:185 upload_file_domain_service
di_containers/module_frame/module_frame_containers.py:208 file_upload_service
di_containers/flow_engine/workflow_excution_containers.py:104 system_config_service
di_containers/flow_engine/job_evidence_containers.py:29,30 file_upload_service / survey_domain_service
di_containers/detection_tools/detection_orchestration_containers.py:56,79,123,124
di_containers/detection_tools/detection_tools_containers.py:169,170,191,192
api/system_config/routes/security_policy_route.py:35 Provide[...security_policy_app_service]
api/system_config/routes/system_config_route.py:97,108,132,144,158 Provide[...restore / system_config_service]
core/plugins/{file_upload,system_core,survey,notification,bulletin,issue,license,asset,identity}.py build_adapters() 的 services= 段
core/app_factory.py 定「lazy 取用」寫法(見首腦核對最後一條),寫一個共用 helper 放 core/plugins/_host.py(例如 package_service(pkg_module, field) 回 lazy provider),全部 48 處用同一支 helper,不要各 container 各寫一套 lambda。git rm -r 目錄、containers.py 拔宣告與 import、core/plugins/<pkg>.py 的 build_adapters() 不填 services=;adapter 若原本從 container 拿 provider(如 core/plugins/asset.py:189 asset.asset_reference_counter()),改在 build_adapters() 內直接建。core/plugins/<pkg>.py 同 ②。api/system_config/ 兩支 route 的六處 Provide[Containers.system_core_container.xxx]:route 層改成從 jedi_system_core.plugin.host_services(current_app) 拿(route 在 request 期,有 current_app),或維持 Provide 但指向主專案新的 lazy provider——擇一,全六處一致。_DI_PACKAGE_INTERNAL_IMPORT_ALLOWED,目標清到只剩 di_containers/auth/(iam 34 處引用屬另案)與第 6-1 棒標記「宿主必填」對應的檔。from jedi_file_upload.infra.repository... import 到任一 container → 紅;加回一行 Provide[upload_file_container.file_upload_service](直綁)→ 需另一條守衛 test_di_containers_do_not_reach_into_package_containers(grep \b(upload_file|system_core|survey|notification|bulletin|issue|license|asset)_container\. 在 di_containers/**、api/** 零命中)→ 紅。