本卡屬 FR-090 母案(開卡後補號)第 2 棒。只搬檔改 import,不改任何邏輯。中量驗證。與第 1 棒動的檔不重疊,可並行。
套件會開「port」(抽象介面)要主專案提供實作,例如「這台設備被幾個稽核任務引用」「登入時怎麼驗 Turnstile」。jedi-asset 的兩個實作放在 infra/asset/,這是對的:它們是主專案接外部系統的膠水,屬 infra 層。但 iam、remote_agent、license、flow_control 四支的實作放在 app/ 下,同一種東西兩種位置。本卡把它們搬到 infra/<pkg>/。
首腦核對(2026-09-12,每檔讀過檔頭、每個 importer grep 過):
app/auth/service/login_adapters.py(97 行):實作 jedi-iam 的 ICaptchaVerifier/MFA 分派/UserScopeResolver 三張 port。importer:core/iam_wiring.py:86、scripts/deliverables/plugin_metrics.py:60(路徑字串清單)app/auth/service/login_policy_provider.py(107 行):實作 ILoginPolicyProvider。importer:app/auth/service/login_service.py、app/user_auth_provider/service/user_auth_provider_service.pyapp/remote_agent/adapter/ 三檔:detection_task_payload_provider.py(IAgentTaskPayloadProvider)、detection_lifecycle_listener.py(IAgentTaskLifecycleListener)、lazy.py(延遲解析代理)。importer:di_containers/remote_agent/remote_agent_containers.py:32-33、di_containers/agent_task/agent_task_containers.py:14、api/remote_agent/__init__.py:39;另有 4 處註解引用路徑(infra/identity/user_name_resolver.py:41、app/license/adapter/notifier.py:15、app/detection_tools/task_type_declaration.py:76、test/test_task_execution_service.py:185)app/license/adapter/notifier.py(53 行):實作 INotifier。importer:infra/license/tenant_directory_adapter.py、api/license/__init__.pyapp/flow_control/adapter/task_existence_query.py(76 行):port 實作。importer:di_containers/flow_control/flow_control_containers.py、di_containers/flow_engine/task_assignee_containers.pyinfra/ 已有 identity/、license/、remote_agent/、flow_control/ 目錄,搬過去不用新建;infra/iam/ 要新建(infra/auth/ 只有 root_admin_reader.py,屬舊命名,本卡不動它)主專案:/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/
搬(git mv):
app/auth/service/login_adapters.py → infra/iam/login_adapters.py
app/auth/service/login_policy_provider.py → infra/iam/login_policy_provider.py
app/remote_agent/adapter/*.py(3 檔) → infra/remote_agent/adapter/
app/license/adapter/notifier.py → infra/license/notifier.py
app/flow_control/adapter/task_existence_query.py → infra/flow_control/task_existence_query.py
改 import(共 10 個檔):
core/iam_wiring.py:86
app/auth/service/login_service.py
app/user_auth_provider/service/user_auth_provider_service.py
di_containers/remote_agent/remote_agent_containers.py:32-33
di_containers/agent_task/agent_task_containers.py:14
api/remote_agent/__init__.py:39
infra/license/tenant_directory_adapter.py
api/license/__init__.py
di_containers/flow_control/flow_control_containers.py
di_containers/flow_engine/task_assignee_containers.py
改路徑字串:
scripts/deliverables/plugin_metrics.py:60
4 處註解(見上)
守衛:
test/test_module_boundaries.py
grep -rn "<舊模組路徑>" --include='*.py' . 每一支重新確認 importer 清單(首腦查過,但你動手時再查一次是紀律)。git mv 五組,infra/iam/__init__.py 新建(空)。搬完 rm -rf 舊位置的 __pycache__。plugin_metrics.py 的路徑字串;4 處註解裡的路徑同步改。app/remote_agent/、app/license/、app/flow_control/adapter/ 搬空後若目錄只剩 __init__.py 就連目錄刪(app/flow_control/ 其他檔還在,只刪 adapter/ 子目錄)。test/test_module_boundaries.py 加一支守衛:AST 掃 app/**/*.py,任何 class 的 base 名字符合 ^I[A-Z]\w+$ 且該 base 是從 jedi_* import 的 → 失敗,訊息寫「port 實作請放 infra/<pkg>/」。突變:把任一 adapter git mv 回 app/ → 紅。python -c "import <新路徑>" 與 python -c "import <每個 importer>",五組批次做完再跑全部驗證。