本卡屬 FR-091(母卡 CM-1690)第三批。中量偏重:這支是 jedi-log 與 jedi-log-forwarding 兩支硬併的,plugin.py 616 行是全部套件最厚的,且它掛的是 log 轉發鏈——掛錯順序或 logger 名改錯,症狀是 log 靜默消失而服務正常。主專案接線在
core/plugins/api_log.py(CM-1689),本卡不動主專案。STATE §2 說 log 要開 identity port——實查 forwarding 已有user_name_resolverport 在補暱稱,不必再開,卡上改判。
jedi-log 底下有兩個子套件:api_log/(API 存取紀錄,有 route 與 DB 表)與 forwarding/(log 轉發鏈,有自己的 plugin.py)。頂層 plugin.py 616 行把兩邊的 dataclass、註冊、轉發鏈啟動、fork 後重啟、migration 全塞一檔。logger 取法有九種(__name__、getLogger() 空、FALLBACK_OPS_LOGGER_NAME、動態 name 等),有些是刻意的(轉發鏈要掛在特定名字下),有些是隨手的。ctx() 12 處。25 處施工日誌。
首腦核對(2026-09-13):頂層 plugin.py 616 行 6 dataclass;forwarding/plugin.py 另一支;api_log/api/__init__.py 含 ctx() 5 處、route 4 處、頂層 plugin 3 處;entity 0;表 api_logs/log_forwarding_settings;migration 3 支;tests 174 支(無 integration);harness 只有 dev_app_forwarding.py+docker-compose-forwarding.yml(轉發鏈用,無 api_log 的、無 --smoke);施工日誌 25 處;logger 九種取法;有吐 created_user_name(forwarding 設定 4 處)但已走 IUserNameResolver port(forwarding/plugin.py:139),不開新 port;tests/test_plugin_contract.py:24 PKG_ROOT 用 log_plugin.__file__(拆檔後指錯,本卡一併改)。主專案 4 檔 import(main.py、core/plugins/api_log.py、common/middleware/app_mw.py、test);venv 指工作樹。
套件:/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-log/(模組名 jedi_api_log)
jedi_api_log/plugin.py(616 行,頂層)
jedi_api_log/forwarding/plugin.py(子插件,有自己的 register)
jedi_api_log/api_log/api/__init__.py(ctx 5 處)、api_log/api/routes/api_log_route.py(ctx 4 處)
範例:jedi-asset/;兩支子插件的分檔參考 jedi-task-platform(participant/ 與 task/ 各自一組 plugin/)——但 task-platform 是大卡尚未整理,只看目錄結構不抄內容
SOP:extraction-sop.md §3.4(🔴 logger 名三連坑,這支就是那段的主角)/§4.3/§8
主專案接線:core/plugins/api_log.py(讀它的 docstring,有兩段紅字講轉發鏈為什麼要晚於 configure_logging、logger 名為什麼用 jedi_api_log)
主專案 import 面:grep -rn "from jedi_api_log\|import jedi_api_log" compliance-manager-be --include='*.py' --exclude-dir=.venv(4 檔);另有 test/test_jedi_package_logger_names.py 守 logger 名
plugin.py → plugin/ 五檔(contract 放 ApiLogServices/ApiLogPluginConfig/ApiLogAdapters/SchemaExtensions/PluginHandle;runtime 放 _RuntimeContext;assembly 放 _assert_*_wiring/create_blueprint/_mount_forwarding_routes/_build_forwarding_context;migrations 放 iter_migrations;__init__ 放 register/start_forwarding_chain/restart_after_fork 本體+re-export)。forwarding/plugin.py 是否也拆由 runner 看完後判斷並回寫理由——若它只是被頂層呼叫的內部件、對外沒人直接 import,可留單檔。api_log/api/__init__.py → guards.py+routing.py。對外 import 路徑一字不改:from jedi_api_log.plugin import register, start_forwarding_chain, restart_after_fork, HANDLE_KEY, ... 是 main.py 與 core/plugins/api_log.py 的契約,拆完逐一實跑。jedi_api_log.forwarding.* 底下是刻意的(core/plugins/api_log.py docstring 講了為什麼),__name__ 六處與 getLogger() 空一處是隨手的,隨手的改 common.jedi_api_log;刻意的保留並在該行留一句陷阱註解。改完跑主專案 test/test_jedi_package_logger_names.py。Path(jedi_api_log.__file__).resolve().parent+突變;harness 補 dev_app.py --smoke(api_log 那支:起 test client 打一條 api-log 列表 401/200,並斷言轉發鏈 handler 有掛上 logging.getLogger("jedi_api_log.forwarding").handlers 非空);pyproject.toml 加 addopts = "--import-mode=importlib";README 補兩支子插件的結構與接線。tests/integration/test_service_behaviour.py(目錄新建):真 postgres 套三支 migration,寫一筆 api_log 讀回、寫一筆 forwarding setting 讀回並斷言 created_user_name 由假 resolver 補上;grant 生效。test_plugin_contract.py 凍結集合同綠。.venv/bin/python -c "import jedi_api_log;print(jedi_api_log.__file__)";指到 site-packages 就加 PYTHONPATH。pytest tests/ --import-mode=importlib(需 docker);harness --smoke;主專案 pytest test/test_module_boundaries.py test/test_jedi_package_logger_names.py;主專案 4 檔 import 逐一實跑(main.py 那條要實際起 BE)。