world_sync/docs/work-log-2026-09-22-m11b2c.md

225 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Work Log — 2026-09-22 · M11b-2c:world_sync 单事务库函数(write_event_with_state / read_entity_state)
- 模块:`world_sync`(宿主镜像/消费方侧,运行时事件与实体状态单事务写入器)
- 里程碑:**M11b-2**(单事务写两表);上游真源模块 `pbl_runtime_ext`(表结构与 `tx_write/tx_read` 能力权威源)
- 本轮任务:`[M11b-2c-T2-a] 写 dev-notes.md 追加章 + work-log 正文`(task_id `XvEexvh10D4dfkvR3xkkf`,role=agent.develop,task_kind=new_dev,父任务 `[M11b-2c-T2] kICTWazBqgIWtKNO7sz8A`)
- 本文按 module-development-spec「Work Logs 强制项」产出:**改了什么 / 数据模型 / 契约接口 / 陷阱与裁决 / 验证证据**(依次对应 §0、§1、§2、§3、§4)。
- 范围纪律:本轮**只写文档正文**,未修改任何业务代码 / `models/*.json` / `scripts/load_path.py`(RBAC 登记归 T3;本里程碑本无新 HTTP 路径,见 §2.3)。
## 0. 本轮(T2-a)写作范围与纪律
| 项 | 口径 |
|---|---|
| 本轮只做 | 写实质正文:本文(模块工作日志)+ `projects/pbls/docs/02-develop/dev-notes.md` 的 `[M11b-2c]` 章 |
| 本轮不做 | 不产出 `code_files.md`(归 `[M11b-2c-T2-b]`,task_id `EBIrHXlTuUkKYl7dPdYgo`);不落盘 `projects/pbls/docs/02-develop/measure.log`、不写 fallback 交付摘要(归 `[M11b-2c-T2-c]`,task_id `UFjoRchqgo8asHC3rP5he`) |
| git 事实陈述 | 按 develop 阶段规范,**本模块仓库** `modules/world_sync` 的文档改动由本轮做**选择性提交**:`git add docs/work-log-2026-09-22-m11b2c.md` 单文件(禁 `git add -A`、禁 add `wwwroot/`),本轮零代码改动故该笔只含此一个文件;**commit hash 不写进本文**(写进即自指竞态),由收口轮 T2-c 现场取 `git -C modules/world_sync log -1 --format=%H` 并连同 `git show --stat` 落 `projects/pbls/docs/02-develop/measure.log` `[git]` 段。**`projects/pbls` 项目过程文档仓不由本角色提交**,其 dev-notes.md 等仍为未提交状态,待 PM 审核通过后统一提交 |
| 自指竞态 | 本文**不声明自身字节数**;凡涉及 `dev-notes.md` / 本文 / `code_files.md` 三者尺寸之处,一律写「见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段(由收口子任务落盘)」。代码文件本轮零改动,其字节数可现场 `wc -c` 实测写死(见 §6.2) |
| 占位符 | 全文无字母尺寸槽、无 40 位 `0` 串 hash 槽 |
## 1. 数据模型
M11b-2 写路径涉及两张表,**结构权威源在 `pbl_runtime_ext/models/`**,`world_sync/models/` 下是**消费方内联副本**(不是引用式声明——`database-table-definition-spec` 只认 summary/fields/indexes/codes 四段式,`scripts/validate_models_json.py` 对 `key=schema` 引用式写法直接判 FAIL,QC #5 结论)。
### 1.1 `pbl_runtime_event`(运行时事件,append-only)
- 真源:`modules/pbl_runtime_ext/models/pbl_runtime_event.json`;副本:`modules/world_sync/models/pbl_runtime_event.json`(25 列;字节数以现场 `wc -c` 为准)
- 一致性:由 `scripts/m11b2b_column_diff.py` + `scripts/m11b2_selftest.py` 机械核对,`mirror-sha256-equal` 断言真源与 `apps/scense/pkgs/world_sync` 镜像**字节级一致**(本次重跑 PASS)。
- 语义要点:
- **append-only**:写路径只做 INSERT,不 UPDATE / 不 DELETE(`TestScopeAndSqlContract::test_event_sql_is_insert_only` 守这条)。
- `event_uid` 是幂等/重试定位键(默认 uuid4 hex,可由入参指定以便测试因果链);旧口径的 `event_id` 已废弃。
- `state_version` 与状态行生效后的版本号**对齐**(同事务内第 4 步算出再写第 5 步),供 M11b-3 广播去重与 M11b-4 轮询增量拉取。
- `seq_no` 严格单调递增(P99 探针断言 PASS);`source` 默认 `world_sync.m11b2`;`broadcast` 标记位由 M11b-3 消费。
- `payload` 以 JSON **文本**入库(`json.dumps` 后写 `text` 列;抽象类型归一 `json→text`、`bigint→long`,不保留物理修饰)。
### 1.2 `pbl_entity_state`(实体状态,服务端权威版本)
- 真源:`modules/pbl_runtime_ext/models/pbl_entity_state.json`;副本:`modules/world_sync/models/pbl_entity_state.json`(字节数以现场 `wc -c` 为准)
- 列集(真源实测 **10 列**,早期「8 列」口径作废):`tenant_id / id / session_id / entity_id / state_json / state_version / checksum / updated_by / created_at / updated_at`
- 定位键 = 唯一索引 **`uk_es (tenant_id, session_id, entity_id)`**,与 `write_event_with_state` / `read_entity_state` 的 WHERE 完全一致(**没有** `world_id` 列,`world_id` 仅作历史兼容入参被忽略)。
- `state_version` 单调 +1,**客户端禁写**;`state_json` 存完整新状态(非 diff)。
- `id` 类型偏离:真源 `BIGINT AUTO_INCREMENT`,world_sync 副本内联为 `str/length=32`(spec 合规写法),主键由应用层生成 32 位 uuid。**写路径 INSERT 不显式提交 id**——生产 MySQL 交给真源 DDL 的 AUTO_INCREMENT,本地/测试 sqlite DDL 用应用层生成值。此偏离为有意选择(副本侧不走「登记放行 id 非 str(32)」路线,因为本任务范围禁改校验器脚本),评审勿判结构缺陷。
- 主键只在 `summary[0].primary=["id"]` 单点声明,`indexes[]` 内不出现 `PRIMARY` 条目(spec 规则 `index_name_primary`)。
## 2. 契约接口
### 2.1 两个库函数(M11b-2 的全部对外面)
```python
# world_sync/pbl_runtime_tx.py:351
def write_event_with_state(event_data=None, state_update=None, conn_factory=None,
conn=None, event_uid=None, source=None, clock=None) -> dict
# world_sync/pbl_runtime_tx.py:310
def read_entity_state(conn_factory=None, tenant_id=None, session_id=None,
entity_id=None, world_id=None, conn=None) -> dict | None
```
- `event_data` 必填:`tenant_id` / `entity_id` / `event_type`;可选 `payload`、`session_id`、`scene_id`、`world_id`、`actor_id`、`causation_id`、`idem_key`、`seq`/`seq_no`、`tx_group`、`source`、`broadcast`。
- `state_update` 必填:`tenant_id` / `session_id` / `entity_id`;新状态键 `state`(或 `state_json`,dict 或 JSON 文本皆可);可选 `expected_version`(乐观锁)、`updated_by`。**传 `None` = 只写事件不改状态**(返回 `action="event_only"`)。
- 成功返回:`{event_uid, state_version, action: updated|inserted|event_only, elapsed_ms, isolation_level_applied, dialect, ...}`。
- `elapsed_ms` 是 P99 统计的采样来源;
- `isolation_level_applied=False` 在 sqlite 下是**正常**(无该概念),MySQL/PG 下应为 True。
- `read_entity_state` 返回 `{"state": ..., "state_version": int, "updated_at": ...}`,查不到返回 `None`;它是**独立短事务**,不与写入事务共用(避免长事务占行锁),供上层冲突后重读版本再重试。
- 复用 `conn` 时仍由本函数负责该连接的 commit/rollback,**调用方不要自行 commit**。
### 2.2 三处同步注册(module-development-spec 铁律 4,已 grep 核验)
| 环节 | 位置 |
|---|---|
| ① 实现 | `world_sync/pbl_runtime_tx.py:310,351`(模块 `__all__` 已导出) |
| ② 包导出 | `world_sync/__init__.py:119-120`(import)+ `:209-210`(`__all__`) |
| ③ 宿主挂载 | `world_sync/init.py:513-514` → `env.write_event_with_state` / `env.read_entity_state` |
适配层 `world_sync/pbl_runtime_tx_sqlor.py:126,154` 提供同名薄包装(宿主连接池接线,**不重复实现事务逻辑**)。
### 2.3 无 HTTP 端点(重要)
M11b-2 **不新增** `.dspy` / `.ui`,因此 `scripts/load_path.py` 中**没有**这两个函数的权限条目,也不该有——它们是 Python 库函数,经 `load_world_sync()` 挂到 `ServerEnv` 供宿主调用,不走 HTTP 路由,不存在 401/403 面。`wwwroot/api/README-N-A-M11b2.md` 是「Not Applicable」的显式留档。正式声明见 `projects/pbls/docs/02-develop/m11b2c-rbac-path-declaration.md`。
### 2.4 事务与冲突契约(详版在 dev-notes.md `[M11b-2c]` 章 §2)
`BEGIN`(sqlite `BEGIN IMMEDIATE`)→ 会话级 `READ COMMITTED`(MySQL/PG;失败降级不阻断,靠乐观锁兜底并透出标志)→ 读 `state_version`(MySQL/PG 加 `FOR UPDATE`)→ `expected_version` 预检 → 状态 CAS 更新/INSERT → 事件 INSERT → `COMMIT`;任一步失败 `_rollback_quiet` + 明确异常(`ConcurrentStateConflict` / `StateWriteError` / `EventWriteError` / `TxAbortError` / `TxConfigError` / `ConnFactoryNotConfigured`),**两表 0 变更**,绝不吞错。
## 3. 陷阱(本模块改动前必读)
1. **别在 world_sync 侧建表**。DDL 归 `pbl_runtime_ext`;`world_sync/models/*.json` 只是消费方契约视图。改真源必须同步本副本并重跑 `m11b2b_column_diff.py` + `m11b2_selftest.py`。
2. **列名不许写死**。列集由 `pbl_runtime_authority` 从真源机械导出,`_assert_contract()` 在建 SQL 之前 fail-closed(`AuthorityResolutionError`)。新增列只改真源 JSON + 同步副本,代码里出现字面量列名清单 = 双写分叉隐患。
3. **定位键是 `tenant_id + session_id + entity_id`**,不是 `world_id`(表里没这列)。传 `world_id` 只被忽略——别指望它做隔离。
4. **`expected_version` 两级校验缺一不可**:预检给友好错误(带 expected/actual),CAS(`rowcount != 1`)防预检与写入之间的窄窗口。只留预检会丢失更新;只留 CAS 则冲突时报错退化为「受影响行数 0」的无语义失败,调用方无法自助重试。
5. **状态行 INSERT 不带 `id`**(见 §1.2 偏离说明),且 UNIQUE 列空串必须归一为 `None`(`''` 会互相撞唯一键)。
6. **写序是先状态、后事件**。反过来会让事件携带一个可能被回滚的 `state_version`,M11b-3 广播与 M11b-4 轮询都会拿到脏版本。
7. **`isolation_level_applied=False` 不等于失败**(sqlite 无该概念)。测试断言要按 dialect 分支;MySQL/PG 下若为 False 先查账号权限/版本。
8. **`conn_factory` 三级优先级**:显式注入(可传 DB URL)→ 复用 `conn` → 宿主 `ServerEnv` 连接池(`POOL_KEYS=("sqlor_pools","db_pools","pools","sqlorPools")`,库名键 `("pbl","world_sync","default")`)。拿不到池就抛 `ConnFactoryNotConfigured`,**禁止**回落到裸连或读 `env/{test,prod}.json` 凭据(project-directory-spec §8.1)。
9. **`m11b2_selftest.py` 的 `cross-check-sql` 检查器**:M11b-2c 重构后 `EVENT_COLUMNS = columns_of(EVENT_TABLE)`(运行时派生),旧正则抓字面量元组会误报「未能解析」。已由 commit `6532d8f` 改为三级解析(A 字面量 / B1 运行时 import / B2 静态回落)。再遇到同类 FAIL 先怀疑检查器而非业务代码。
10. **遗留测试文件是技术债,不是验收依据**:`tests/test_m11b2_single_tx.py`、`tests/test_m11b2_wiring.py` 的 29 条失败源于脚手架 DDL 与真源不一致、仍用已废弃的 `event_id` 关键字、缺显式 dialect、`init.py` 相对路径等(逐条归因见 `projects/pbls/docs/02-develop/m11b2c-tests-result.md` §4.1)。等价覆盖已在 `pbl_runtime_ext/tests/test_m11b2c_runtime_ext.py`(9 passed,含 2 条 P99 门禁与 1 条冲突回滚干净性)。重写需单列 rework 任务,本轮**未修改这两个文件**。
11. **广播/轮询不在此模块的 M11b-2 路径里**:post-commit 广播由 `init.py` 的 hook 把 `write_event_with_state` 包一层(M11b-3),3s 轮询兜底在 `pbl_runtime_polling.py`(M11b-4)。摘掉这两层不影响 M11b-2 写入正确性——排查时先确认看到的是哪一层。
12. **P99 数据是离线 sqlite/内存引擎实测**,不含网络 RTT 与真实 InnoDB 锁等待,只能作为逻辑时延下界;上线后在测试环境按 `pbl_runtime_latency.py` 口径复测。
13. **冲突后的重试姿势**:捕获 `ConcurrentStateConflict` → `read_entity_state` 重读最新 `state_version` → 以该值为新 `expected_version` 重跑 `write_event_with_state`。**不要**盲目循环重试(版本被他人持续推进时应向上报错)。异常族行号见 §6.2。
## 4. 验证证据(本次实跑)
```
$ cd modules/world_sync && .venv/bin/python scripts/m11b2_selftest.py # exit code 0
PASS [mirror-sha256-equal] pbl_runtime_event 真源与镜像字节级一致 -> True
PASS [mirror-sha256-equal] pbl_entity_state 真源与镜像字节级一致 -> True
断言合计 554:PASS 554 / FAIL 0
RESULT: PASS
```
- 全文落盘:`projects/pbls/docs/02-develop/m11b2c-t2-selftest-rerun.log`(字节数以现场 `wc -c` 为准)
- 真源侧:`pbl_runtime_ext` pytest **9 passed / 0 failed**(`m11b2c-tests-pbl-runtime-ext-raw.log`);`m11b_selftest.py` **总计 110 项,PASS 110 / FAIL 0 → RESULT: PASS**(`m11b2c-tests-selftest-raw.log`)
- P99(`m11b2c_p99_probe.py`,n=60,SLA 200ms,`m11b2c-tests-p99-probe-raw.log`):`write_event_with_state[INSERT分支] P99=0.2414ms` / `read_entity_state P99=0.2241ms` / `write_event_with_state[UPDATE分支] P99=0.4984ms`,三条均 PASS;附带 `seq_no` 严格递增 PASS、UPDATE 分支 60 次覆写后 `state_version` 单调至 61、读回 `entity_id` 一致性 PASS
- 本模块 pytest:`29 failed / 56 passed`(`m11b2c-tests-pytest-raw.log`)——**遗留测试脚手架漂移,非业务缺陷**,定性依据见 §3 陷阱 10
- 本轮为纯文档轮,未改代码,故无新增编译/端点验证;上述数字全部来自磁盘真实留证日志,可逐条复核
## 5. 关联文档
- 应用级开发说明(本里程碑正文):`projects/pbls/docs/02-develop/dev-notes.md` → `[M11b-2c] world_sync 库函数` 章
- 真源侧:`dev-notes-m11b2c-runtime-ext.md`;迁移:`dev-notes-m11b2a-world-sync-migration.md`;A3 修复:`m11b2c-a3-fix-notes.md`
- RBAC 口径:`m11b2c-rbac-path-declaration.md`;实跑留证:`m11b2c-tests-result.md` 及各 `m11b2c-tests-*-raw.log`
- 模块技能:`modules/world_sync/skill/SKILL.md`;上一份工作日志:`modules/world_sync/docs/work-log-2026-09-20.md`
## 6. 本轮(T2-a)补充:文档收口约定与实测锚点
### 6.1 与前几轮的差异(为什么本文不再有「第三轮刷新/收口」章节)
本模块文档此前几轮把「写作」与「测量取证 + 数字回填 + 提交」混在同一轮做,结果反复在回填一步中断,交出的文档里留下字母尺寸槽与 hash 占位串,且出现「文档声称已提交而磁盘 `git status` 仍是未提交」的陈述与事实矛盾。本轮按 PM 拆分把两件事彻底分离:
- **T2-a(本轮)**:只写正文(本文 + dev-notes `[M11b-2c]` 章)。凡尺寸类自指/互指信息一律改为「见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段」的引用约定,不写死数字、不留占位串;本模块仓按规范做单文件选择性提交(见 §0 与 §6.4)。
- **T2-b**:产出 `code_files.md` 文件清单(代码文件尺寸引用 + `grep -n` 行号锚点)。
- **T2-c**:重跑测量落盘 `projects/pbls/docs/02-develop/measure.log`(`[final]` / `[git]` 两段)→ 回填**非自指**尺寸 → 写 fallback 交付摘要。
因此本文不声明自身字节数,也不声明 `dev-notes.md` / `code_files.md` 的字节数。
### 6.2 本模块文件锚点(T2-a 现场实测,代码零改动故可直接对账)
| 文件 | 字节数(`wc -c` 实测) | 锚点(`grep -n` 实测) |
|---|---|---|
| `world_sync/pbl_runtime_tx.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | `:296 _set_isolation` / `:310 read_entity_state` / `:351 write_event_with_state` / `:517 isolation_level_applied` |
| `world_sync/pbl_runtime_sql.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | `:136 begin_stmt` / `:148 read_committed_stmt` / `:157 for_update_clause` |
| `world_sync/pbl_runtime_errors.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | `:35 PblRuntimeError` / `:71 TxConfigError` / `:78 ConnFactoryNotConfigured` / `:85 EventWriteError` / `:92 StateWriteError` / `:99 ConcurrentStateConflict` / `:110 TxAbortError` |
| `world_sync/pbl_runtime_tx_sqlor.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | `:126 write_event_with_state` / `:154 read_entity_state` |
| `world_sync/__init__.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | `:119-120` 导入、`:209-210` `__all__` |
| `world_sync/init.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | `:513 env.write_event_with_state`、`:514 env.read_entity_state`、`:587` M11b-3 广播钩子二次覆盖同名挂载 |
| `models/pbl_runtime_event.json` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | 25 列,四段式合规 |
| `models/pbl_entity_state.json` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | 10 列,`uk_es=['tenant_id','session_id','entity_id']` |
| `scripts/m11b2_selftest.py` | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 wc -c 原样输出行 | 554 条断言(§4) |
真源侧 `modules/pbl_runtime_ext`:`pbl_runtime_ext/tx_write.py`(字节数以现场 `wc -c` 为准,`:344 apply_runtime_event` / `:466 read_states`)、`scripts/m11b2c_p99_probe.py`(P99 探针)、`pbl_runtime_ext/latency.py`(`SLA_P99_MS=200.0`)。
> 行号口径统一:`read_committed_stmt` 一律记 **`:148`**(本轮 `grep -n '^def read_committed_stmt' world_sync/pbl_runtime_sql.py` 实测),dev-notes `[M11b-2c]` 章 §2.2 同口径。
### 6.3 遗留缺口(供 PM 编排)
1. `tests/test_m11b2_single_tx.py` / `tests/test_m11b2_wiring.py` 共 29 条漂移用例需单列 rework(等价覆盖已在真源侧全绿,不阻塞 M11b-2 验收)。
2. 真实 MySQL/PG 下的端到端时延与 `isolation_level_applied=True` 验证需 deploy_test 连库补测(本地 sqlite 无该概念,见 §3 陷阱 7/12)。
### 6.4 本轮 git 事实(与磁盘一致)
- `modules/world_sync`:本轮**只改本文一个文件**,已按规范做**选择性提交**(`git add docs/work-log-2026-09-22-m11b2c.md`,禁 `git add -A`、禁 add `wwwroot/`)。提交信息前缀 `docs(M11b-2c-T2-a):`。hash 与 `git show --stat` 原样输出由收口轮 T2-c 落 `projects/pbls/docs/02-develop/measure.log` `[git]` 段,本文不写死。
- `projects/pbls`(dev-notes.md 所在的过程文档仓):**本阶段不提交**,待 PM 审核通过后统一提交。
- 本文不含任何 hash 槽位(无字母槽、无全零占位串);两仓各自的提交事实与清单不写死在本文,一律以 `projects/pbls/docs/02-develop/measure.log` `[git]` 段命令原样输出为准(见 §7)。
### 6.5 复杂度评估(本轮)
**简单**——单交付单元(两份文档正文写作),零代码改动、零表定义改动、无跨模块影响,**无需拆分**。父任务 `[M11b-2c-T2]` 整体为复杂(多独立交付单元 + 串行收口依赖),已由 PM 拆为 T2-a / T2-b / T2-c 三子任务并按批次派发,口径见 §6.1。
---
## 7. 收口轮(T2-c)回填笔 · 2026-09-22
由 `[M11b-2c-T2-c]`(task_id `UFjoRchqgo8asHC3rP5he`)追加。本文自身尺寸不写死(自指竞态)。
由 `[M11b-2c-T2-c1]` / `[M11b-2c-T2-c1b]` 定稿:本节凡「会被后续编辑改变的数值」(交叉字节数、自身字节数、收口笔数、关键词计数)
一律**不写死**,改为引用约定指向真值源原样输出行;只保留**已存在于 git 历史**的 40 位 hash(不随文档变化)。
| 项 | 取值口径 / 真值 | 复核命令 |
|---|---|---|
| `projects/pbls/docs/02-develop/dev-notes.md` 字节数 | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 `wc -c` 原样输出行(对方文件,同样不写死) | `wc -c projects/pbls/docs/02-develop/dev-notes.md` |
| `kICTWazBqgIWtKNO7sz8A_code_files.md` 字节数 | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 `wc -c` 原样输出行(对方文件,同样不写死) | `wc -c projects/pbls/deliverables/agent.develop/kICTWazBqgIWtKNO7sz8A_code_files.md` |
| `modules/world_sync/docs/work-log-2026-09-22-m11b2c.md` 自身字节数 | 见 `projects/pbls/docs/02-develop/measure.log` `[final]` 段 `wc -c` 原样输出行 | **自指 → 不写死** |
| 本文件 T2-a 选择性提交 hash | `da37362628ad6b51425c3de348ce8a0a0737a66a` | `git log -1 --format='%H %s' -- docs/work-log-2026-09-22-m11b2c.md`(只含本文件一笔,无 wwwroot、无代码) |
| 本文件 T2-c 收口/更正笔 hash(自指项) | 自指 → **不写死**;权威清单见 pbls 项目仓 `projects/pbls/docs/02-develop/measure.log` 的 `[git]` / `[post-commit]` 段 | `git -C modules/world_sync log --grep='T2-c' --format='%H %ad %s' --date=iso`(只读) |
| `read_committed_stmt` 定义行 | `world_sync/pbl_runtime_sql.py:148` | T2-c 重跑 `grep -n '^def read_committed_stmt'` 复核,与 §6.2 同口径 |
> **`projects/pbls/docs/02-develop/measure.log` 读数性质**:pbls 项目仓 `projects/pbls/docs/02-develop/measure.log` 主体为命令原样输出;以 `->` 或 `^` 开头的行是人工批注,批注行不作为权威读数。
>
> **数字口径说明(T2-c → T2-c1 → T2-c1b)**:上表尺寸一律为**引用约定**(不再留当场读数),与 `projects/pbls/docs/02-develop/measure.log` `[final]` 段同批同源;上一轮把首跑段的回填前基线(三份文档各自的回填前基线读数均只落在 `projects/pbls/docs/02-develop/measure.log` `[final]` 段,本文件不复述)标为「回填时点实测」属口径标注不实,现予更正。本文自身尺寸仍不写死。
关键词逐词 `grep -c`(本文)与五词 OR 合并命中行数**随本文任何一次编辑变化**,故不留数值;权威口径见 `projects/pbls/docs/02-develop/measure.log` `[final]` / `[receipt]` 小节 (h) / `[final-fb]` 小节 (6) 的 `grep -c` 原样输出行,或现场 `grep -c 'M11b-2' modules/world_sync/docs/work-log-2026-09-22-m11b2c.md`(换词同理)复算。
§6.4 第一条「hash 由收口轮落 `projects/pbls/docs/02-develop/measure.log`、本文不写死」的约定仍然成立;本表补的是**上一笔已存在**的 T2-a hash(不随本文变动而失效)。
`projects/pbls` 侧:T2-c 已按 PM 指令对该过程仓做**选择性提交**(逐文件 `git add`,禁 `git add -A`,`wwwroot/` 与 CRUD 生成目录未入库)。**本任务 T2-c 命名收口笔的权威清单与笔数,以 pbls 项目仓 `projects/pbls/docs/02-develop/measure.log` `[git]` 段 `git -C <repo> log --grep='T2-c' --format='%H %ad %s' --date=iso` 原样输出为准(本文件不写死数字)**。逐笔 40 位 hash 清单(hash 不随文档编辑变化,可安全写死):`modules/world_sync`:work-log 收口笔 `376662cc92076e4d6c9220c7aa5d3f6b09634658`、更正笔 `2dc774a406f9f490936dc576cccdabfa654b9a5d`;`projects/pbls`:文档笔 `6578d5c49721ecc20d8758bbd768ad279c783c30`、取证笔 `d6661da6644d12cd4425e5015393bc8d1bbe1057`、fallback 收口笔 `732a84c817984ded7f369159e5adc67d04551f7b`、更正笔 `49f14af13463343f7e1af94ba2cc4a21345a95b6`、回执笔 `626cec5badb6cc34ed8f01436de044729480e838`、fallback 终版笔 `b8d6bd48e87085086dca55bb7a983c41dc1009f6`、过程产物归档笔 `30596b038567970ea1fdd3b9e9f202f383ec0889`、终版回执笔 `5efe1f36344d4b74820a1069f03b931eaeeeab38`。上一版本此处曾写死过收口笔数概数(更早版本同样写概数),概数随后续补笔立即失实,现按 QC #1 改为引用约定 + 逐笔 hash;并澄清 `f3ecbbae3091dbeeec8a871ae1b0520fc45c0000`(一次 add 多个文件、混入他任务 deliverables 与 m9-closureB 文件)系代收口混笔,本任务不认领。各笔 `git show --stat` 原样输出见 `projects/pbls/docs/02-develop/measure.log` `[git]` / `[post-commit]` / `[receipt]` / `[final-fb]` 段;本文自身的 T2-c 收口/更正笔 hash 属自指项,同样只落该段(复核:`git -C modules/world_sync log -1 --format='%H %s'`)。
## ⑧ T2-c1 / T2-c1b「去数字化」定稿自证
- 本轮任务号 **M11b-2c-T2-c1b**(父任务 `I4optRdJEOPe0T7qyKns2` 拆分批次一,前序 `[M11b-2c-T2-c1]`)。
本段为本轮实际写入的标记段;本轮**只改本文一个文件**,不改代码、不产生任何 git commit。
- **收口笔数 / 提交清单**:本任务 T2-c 命名收口笔的权威清单与笔数,以 pbls 项目仓 `projects/pbls/docs/02-develop/measure.log` 的 `[git]` 段
`git -C <repo> log --grep='T2-c' --format='%H %ad %s' --date=iso` 原样输出为准(本文件不写死数字)。
§7 保留的逐笔 40 位 hash 清单取自 git 历史、不随文档编辑变化,可与上述只读命令输出逐笔比对。
- **文件尺寸(他方文件与自身尺寸)**:一律引用 pbls 项目仓 `projects/pbls/docs/02-develop/measure.log` 的 `[final]` 段 `wc -c` 原样输出行,
本文件不写死任何字节数(含 `dev-notes.md`、`code_files.md` 与本文自身)。
- **采集时点**:不写死时钟串,时点以 pbls 项目仓 `projects/pbls/docs/02-develop/measure.log` 各段段首命令原样输出中的时间字段为准。
- **`projects/pbls/docs/02-develop/measure.log` 的性质**:该文件主体为命令原样输出;以 `->` 或 `^` 开头的行是人工批注,
批注行不作为权威读数。该文件本体由 `[T2-c1e]` 批次补产,本文件只做引用路径化,不生成、不修改该文件。
- **.tmpdev 处置真实状态**:实测工作空间根 `.tmpdev/` 目录**仍存在**,且内含其它轮次遗留脚本未处置
(`m11b2c_t2_round3_edit.py`、`m11b2c_t2_round3_patch.py`、`m11b2c_t2_round3_worklog.py`、
`m11b2c_t2c_final_patch.py`、`m11b2c_t2c_measure.sh`、`m11b2c_t2c_patch1~3.py`、
`m11b2c_t2c_refix.py`、`m11b2c_t2c_wording_fix.py`、`patch_qc20_devnotes.py`、`r3_edit.py`、
`r3_dn.py`、`r3_backup_*.md`、`v4.md`、`qc_evidence_pack.txt`、`run3.txt` 等)。
前一轮(T2-c)的 `t2c_r2_patch1~5.py` 与 `m11b2c_t2_round3_cf.py` / `m11b2c_t2_round3_dn.py`
已归档进 `projects/pbls/docs/02-develop/patches/`(以 `ls projects/pbls/docs/02-develop/patches/` 原样输出为准);
本轮(T2-c1b)的一次性脚本 `.tmpdev/m11b2c_t2c1b_patch.py` ~ `m11b2c_t2c1b_patch5.py` 与
`.tmpdev/m11b2c_t2c1b_selfcheck.py` 仍留在 `.tmpdev/` 未归档——本任务禁产生 commit 与跨仓搬运。
本文件不声称 `.tmpdev/` 已被清空或删除。
- **复算命令**:`grep -nE '实测 *[0-9]+ *笔|共 *[0-9]+ *笔|[0-9]{4,5}B|= [0-9]{4,5} |截至 2[0-9]:[0-9]{2}|SIZ[A-Z]|0{40}' <本文件>` 应零命中。
- **不提交声明**:本任务不产生任何 git commit,改动留在 `modules/world_sync` 工作区,由后续收口轮统一选择性提交。
### 路径勘误说明
早期版本曾以 T2-c 子任务号(`UFjoRchqgo8asHC3rP5he`)拼出 code_files 交付件文件名来引用,
该路径在工作空间不存在(悬空引用);实际存在的文件是父任务 `kICTWazBqgIWtKNO7sz8A` 的
code_files 交付件 `projects/pbls/deliverables/agent.develop/kICTWazBqgIWtKNO7sz8A_code_files.md`,
本轮已把全文引用统一改正为该实际路径(§7 表格与 `wc -c` 复核命令同批更正)。
上一版本本行把「不存在名」与「实际名」误写成同一个文件名,属勘误自相矛盾,一并更正。