# work-log 2026-09-20 — world_sync(M11b-2a 迁移 + M11b-2a-1 根包合规 + M11b-2a-2 文档归档) - 仓库:`modules/world_sync`(remote `yumoqing/world_sync`,branch `main`) - 记录人:agent.develop | 记录时间:2026-09-20 19:20 (+08:00) - 关联任务:`[M11b-2a]` u6kCWdLV2k5cmuG8Yb5BY(父)/`[M11b-2a-1]` dPgvSjRDNQ6kIQi0K-6HT(approved)/`[M11b-2a-2]` hVZNlBKmtiNNzn6BPeA3J(本记录) - 关联迭代:pbls-初始迭代 | 响应 QC 未过项:#1 开发说明缺失、#2 过程归档缺失(#3 由 M11b-2a-1 处置) --- ## 1. Scope(本任务链做了什么 / 不做什么) **做:** 1. 把 M11b-2「单事务事件+状态原子写入」实现从应用侧 `apps/scense/pkgs/world_sync/world_sync/` 迁移到模块仓库 `modules/world_sync/world_sync/`(5 个新增子模块 + `__init__.py` + `init.py`),tests/scripts 同迁 —— 落实「develop 模块源码必须落 modules/」。 2. 三处接线:内层包 re-export 契约符号、`init.py` loader 命名与 ServerEnv 登记、根包导出(后者经核对判定违规后撤销,见 §3)。 3. 结构合规处置:删除仓库根 `__init__.py`(156 行转发包)+ `pyproject.toml` 收紧打包面 + 导入闭包重验。 4. 文档归档(M11b-2a-2):`projects/pbls/docs/02-develop/dev-notes-m11b2a-world-sync-migration.md` + 本 work-log。 **不做:** 业务逻辑(事务/行锁/回滚/乐观并发/权限判定)零改动;`models/` JSON 表定义零改动(M11b-2b);apps 侧部署副本清理未强制(§5 遗留项 3)。 --- ## 2. Commit 列表 | commit | 时间 | 内容 | |---|---|---| | `01941aa` | 2026-09-20 | `deliver: 交付收口(引擎代为提交)` —— 迁移本体落库:`world_sync/{pbl_runtime_errors,pbl_runtime_sql,pbl_runtime_tx,pbl_runtime_tx_env,pbl_runtime_tx_sqlor}.py` + `__init__.py`(6187B) + `init.py`(22123B) + tests/scripts;三处接线成立(含当时的根包转发,156 行) | | `9f97d58` | 09-20 18:31 | `M11b-2a: resolve root-package spec compliance (drop repo-root __init__.py forwarding package)` —— **删除** `modules/world_sync/__init__.py`(1 file changed, 156 deletions);commit message 内记录宿主接线行号证据与删除后复验结论;用 pathspec 有意排除 M11b-2b WIP `scripts/validate_models_json.py` | | `95b405f` | 09-20 | `deliver: 交付收口(引擎代为提交)` —— M11b-2a-1 交付件收口 | | `aff1f0d` | 09-20 19:12 | `M11b-2a: resolve root-package spec compliance (rev2: tighten packaging surface, record real import evidence)` —— `pyproject.toml:25-28` `[tool.setuptools.packages.find] where=["."] include=["world_sync*"] namespaces=false`;**更正 rev1 不实的 case1 输出**,补 host(venv editable)取证 | | 本任务 | — | 仅新增文档:`projects/pbls/docs/02-develop/dev-notes-m11b2a-world-sync-migration.md`、`modules/world_sync/docs/work-log-2026-09-20.md`(git 收口由引擎执行,本记录不自称已 commit/push) | --- ## 3. 关键决策与陷阱 ### 3.1 为何「删除」根包转发,而不是「保留 + 收紧」(QC #3 给了二选一) - **规范**:module-development-spec 规定 `mymodule/` 是仓库根、`mymodule/mymodule/` 才是 Python 包,仓库根不列 `__init__.py`;同机构 `modules/pbl_runtime_ext/` 等仓库根均无 `__init__.py`。 - **宿主接线实测不依赖它**:`apps/scense/app/scense.py:50 from world_sync.init import load_world_sync`(L57 调用)、`apps/yuanjing/app/yuanjing.py:66 __import__(f'{module}.init', ...)` —— 两者都以 sys.path=仓库根解析到**内层** `world_sync/init.py`。转发表无人调用 = 死代码。 - **风险**:根包 + `packages.find include=["world_sync*"]` 组合会把仓库根一并发现为包,产生「同一模块两条导入路径、两份 `__init__` 语义」的双路径歧义,后续 develop 极易误用根包路径写出跑不通的接线。 - **决策**:走 (a) 删除(`9f97d58`),并追加 (b) 的配置部分(`aff1f0d`)把约束固化进 `pyproject.toml`,防回归。 ### 3.2 陷阱:`init/` 数据目录遮蔽 `init.py`(case1 的真实 FAIL) `modules/world_sync/init/data.json` 是模块既有数据目录。当 sys.path 落在**模块父目录** `modules/` 时,`world_sync.init` 优先解析到该目录(namespace 片段)而非 `init.py`,于是 `from world_sync.init import load_world_sync` → `ImportError: cannot import name 'load_world_sync' from 'world_sync.init' (unknown location)`。 用删除前基线 `01941aa` 沙箱复刻同场景:**删除前同样 FAIL** ⇒ 非本次删除引入的回归。结论:world_sync 的 loader 接线以 **sys.path=仓库根**(宿主实际做法)为准,`sys.path=modules/` 不作为契约路径;若要支持需单独立项(重命名数据目录或调 `scripts/load_path.py` 注入顺序)。 ### 3.3 陷阱:把「删除前的验证输出」当成「删除后的证据」(流程教训) M11b-2a-1 rev1 文档 §4.2 与交付摘要 §3 中 case1 的 OK 输出块为不实记录(错误归因),被 QC #7/#8/#14 指出。rev2 已删除伪造块、以 `bash projects/pbls/scripts/m11b2a1_rev2_revalidate.sh` 落盘输出替换(全文 `projects/pbls/evidence/m11b2a1_rev2_import_closure.txt`)。本 work-log 与 dev-notes 中所有输出为 2026-09-20 19:20 **再次独立复跑**,与 rev2 一致。教训:验证必须在处置动作之后重跑,且证据要落盘可复核。 ### 3.4 决策:单一真源 契约函数实现只在 `world_sync/pbl_runtime_tx_sqlor.py`,`__init__.py:40 from .init import (...)` / L119–120 只做 re-export(实测 `write_event_with_state.__module__ == 'world_sync.pbl_runtime_tx_sqlor'`),不允许在包 `__init__` 或根包中写第二份实现。 --- ## 4. 验证(2026-09-20 19:20 实测) | 项 | 命令 | 结果 | |---|---|---| | 语法 | `python3 -m py_compile world_sync/*.py scripts/*.py` | `rc=0` 全通过 | | case2 sys.path=仓库根 | `import world_sync` / `from world_sync.init import load_world_sync` / 4 契约符号 | OK;`__file__` → `modules/world_sync/./world_sync/__init__.py`;仓库根 `__init__.py` 存在性 = `False`;`__all__ len=62 MISSING=[]`;`issubclass(ConcurrentStateConflict, PblRuntimeError)=True` | | case1 sys.path=`modules/` | 同上 | **FAIL**(`_NamespacePath`/`__file__=None`/`ImportError (unknown location)`),根因 §3.2,非回归 | | host sys.path=`apps/scense/pkgs/world_sync` | 同上 | OK;`__file__` → apps 副本内层包;`__all__: 62 MISSING: []` | | 迁移一致性 | `diff -q` 8 个 .py(模块仓库 vs apps 副本) | 8/8 IDENTICAL | | 工作区 | `git status --porcelain` | 无输出 = clean;branch `main`,`remotes/origin/main` 存在,另有 `archive/m11b1-in-world-sync` | 完整输出块见 `projects/pbls/docs/02-develop/dev-notes-m11b2a-world-sync-migration.md` §4。 --- ## 5. 受环境限制未验证项(不得当作已验证) 1. **真实 DB 建连与写入路径未跑**:沙箱无 MySQL/PG。`configure_runtime_writer`(`init.py:326`)、单事务内「事件写 + 实体状态写 + 行锁」原子性、失败回滚、`ConcurrentStateConflict` 实际抛出路径 —— 均只有静态/import/mock 级覆盖,需部署态联调。 2. **`tests/test_m11b2_single_tx.py` / `test_m11b2_wiring.py` 未执行**:依赖 DB fixture,本轮只保证可编译可导入。 3. **`env.pbl_runtime_conn_factory` 端到端解析未跑**:`init.py:480/483` 的 ServerEnv 键解析链需应用启动态验证。 4. **apps 侧薄壳清理未做(原任务第 4 项为可选)**:`apps/scense/pkgs/world_sync/world_sync/` 保留 8 个 IDENTICAL 文件,因为宿主 `scense.py:50` 实际加载的就是该 editable 副本;删除会让 scense 立即失去模块。建议后续改为部署期同步产物(`build.sh` / `scripts/sync_models_to_app.py`)+ `.gitignore` 排除,做到「模块仓库唯一真源」。 5. **`models/` JSON 与 SQL 列清单 diff**:留 M11b-2b(`scripts/m11b2b_column_diff.py` 已就位)。 6. **`sys.path=modules/` 契约路径**:已确认不可用并文档化(§3.2),未修复。 --- ## 6. 当前 branch-commit 状态(记录时点) ``` branch: main(tracking remotes/origin/main);工作区 clean HEAD: aff1f0d M11b-2a: resolve root-package spec compliance (rev2: tighten packaging surface, record real import evidence) 95b405f deliver: 交付收口(引擎代为提交) 9f97d58 M11b-2a: resolve root-package spec compliance (drop repo-root __init__.py forwarding package) 01941aa deliver: 交付收口(引擎代为提交) 仓库根结构:README.md / pyproject.toml / init/(数据目录) / json/ / models/ / scripts/ / skill/ / tests/ / wwwroot/ / world_sync/(内层包) / docs/(本记录) 无仓库根 __init__.py —— 对外唯一路径:world_sync.init(loader)、world_sync(内层包,sys.path=仓库根)、world_sync.world_sync.*(子模块,sys.path=仓库根时等价 world_sync.*) ``` 本任务(M11b-2a-2)新增文档后的 commit 由引擎交付收口统一执行,本记录不自称已 commit/push。