deliver: 交付收口(引擎代为提交)

This commit is contained in:
agent.develop 2026-09-22 21:00:10 +08:00
parent cc2e1c3184
commit b1d4490328
2 changed files with 306 additions and 0 deletions

View File

@ -163,3 +163,24 @@ RESULT: PASS
### 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`)追加。本文自身尺寸不写死(自指竞态),见
`projects/pbls/docs/02-develop/m11b2c-t2-code-files-measure.log` `[final]` 段;以下只回填**对方**实测值与**已存在**的真实 hash。
| 项 | 真值 | 口径 |
|---|---|---|
| `projects/pbls/docs/02-develop/dev-notes.md` 字节数 | 54239 | `wc -c` T2-c 回填时点实测(对方文件,非自指) |
| `kICTWazBqgIWtKNO7sz8A_code_files.md` 字节数 | 20047 | 同上 |
| `modules/world_sync/docs/work-log-2026-09-22-m11b2c.md` 自身字节数 | 见 measure.log `[final]` 段 | **自指 → 不写死** |
| 本文件 T2-a 选择性提交 hash | `da37362628ad6b51425c3de348ce8a0a0737a66a` | `git log -1 --format='%H %s' -- docs/work-log-2026-09-22-m11b2c.md`(只含本文件一笔,无 wwwroot、无代码) |
| 本文件 T2-c 收口笔 hash | 见 measure.log `[git]` 段 | 自指(写进本文即改变本文) |
| `read_committed_stmt` 定义行 | `world_sync/pbl_runtime_sql.py:148` | T2-c 重跑 `grep -n '^def read_committed_stmt'` 复核,与 §6.2 同口径 |
关键词逐词 `grep -c`(本文):`M11b-2` 17 / `expected_version` 4 / `READ COMMITTED` 1 / `P99` 6 / `无新 HTTP 路径` 1。
§6.4 第一条「hash 由收口轮落 measure.log、本文不写死」的约定仍然成立;本表补的是**上一笔已存在**的 T2-a hash(不随本文变动而失效)。
`projects/pbls` 侧:T2-c 已按 PM 指令对该过程仓做选择性提交(只加本任务四个文件),故 §6.4 中「仍为未提交状态」一句已被收口轮更新取代,真实 hash 见 measure.log `[git]` 段。

View File

@ -0,0 +1,285 @@
# -*- coding: utf-8 -*-
"""M11b-2d-A3 契约守卫用例:函数式夹具契约 + 真源返回键齐全性。
来源
====
本文件由一次性调试探针 ``modules/world_sync/.m11b2d_probe.py``(3250B,随引擎代提交
``13386a4`` 入库)改造而来——探针用 ``print`` 输出、无断言、不可重复判定,QC #4 要求
「要么删除、要么升级为正式守卫」。本文件即升级结果,探针本体已 ``git rm``。
守卫什么(三件事)
==================
1. **旧类式符号不再存在**:``RuntimeStateEventWriter`` / ``configure_writer`` /
``build_plan`` / 类式 ``write_event_with_state`` 不得再出现在夹具层
``tests/_m11b2_compat`` 上(它们曾构成第二条夹具路径,把测试钉在已删除的调用形态上,
实测 22 failed)。
2. **新函数式符号可用**:夹具只保留一条路径——``install/uninstall/apply/read/
trace_kinds/update_statements/insert_statements`` + ``RuntimeEngine``,且真源本体
``apply_runtime_event`` / ``read_states`` 经夹具直连(不是副本)。
3. **真源返回键齐全**:``apply`` 的返回值键集合**逐键断言**,不做任何增补(夹具
``apply`` 只是 ``asyncio.run(apply_runtime_event(...))`` 的同步入口)。分支覆盖
成功写 / 幂等命中 / 乐观锁冲突 / 参数门禁 / 快照 / 提交后广播。
边界(遵守 QC #3 允许路径与本任务禁改清单)
==========================================
* 不改业务实现、不改 ``pbl_runtime_ext/models/*.json``、不改 ``load_path.py``;
* 不改 ``tests/_m11b2_compat.py`` 任何既有符号(只读引用);
* 不改 ``test_m11b2_single_tx.py`` / ``test_m11b2_wiring.py``(并行任务独占);
* 权威契约定义在 ``tests/_m11b2_compat.py`` 顶部 docstring 与
``docs/work-log/``… 不,登记文档为 ``skill/SKILL.md`` 指向的
``docs/work-log/m11b2d-fixture-contract.md``,本文件只做机械断言,不重复搬运说明。
夹具的 ``_default_clause`` 把 models 里 ``default: ""``(空串)当「无默认值」跳过,
于是 ``event_code`` 等「NOT NULL DEFAULT ''」列在 sqlite 侧成为无默认值的 NOT NULL 列,
真源 INSERT(按语义不填这些列)会撞约束。本文件与 ``test_m11b2_single_tx.py`` 同口径:
**仅在构造期**临时补正空串默认值渲染,建完表立即还原,不改 compat 既有定义与行为语义。
"""
import inspect
import os
import sys
import unittest
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
sys.path.insert(0, os.path.dirname(HERE))
import _m11b2_compat as compat # noqa: E402
apply_runtime_event = compat.apply_runtime_event # pbl_runtime_ext.tx_write 真源本体
read_states = compat.read_states
RtxError = compat.RtxError
EVENT_TABLE = compat.EVENT_TABLE
STATE_TABLE = compat.STATE_TABLE
SNAPSHOT_TABLE = compat.SNAPSHOT_TABLE
ERR_TENANT_MISSING = compat.ERR_TENANT_MISSING
ERR_BAD_PARAM = compat.ERR_BAD_PARAM
ERR_STATE_CONFLICT = compat.ERR_STATE_CONFLICT
# 真源 tx_write.apply_runtime_event 成功分支的返回键(源码 tx_write.py:456-462 逐键抄录)
FULL_RETURN_KEYS = {
"ok", "dedup", "event_id", "event_uid", "seq", "seq_no", "cursor", "state_rows",
"states", "latency_ms", "broadcast_ms", "channel", "sla_breach", "stmt_count",
}
# 幂等命中分支(源码 tx_write.py:383-389):无 states/broadcast_ms/sla_breach/seq_no
DEDUP_RETURN_KEYS = {
"ok", "dedup", "event_id", "event_uid", "seq", "cursor", "state_rows",
"latency_ms", "channel", "stmt_count",
}
# 真源 apply_runtime_event 形参(源码 tx_write.py:344-348),全部默认 None
TRUE_SOURCE_PARAMS = [
"tenant_id", "world_id", "session_id", "event_type", "payload", "state_updates",
"client_event_id", "idem_key", "snapshot", "base_version", "actor_id",
"causation_id", "occurred_at", "env",
]
# 夹具层禁止复活的旧类式符号
DEAD_CLASS_STYLE_SYMBOLS = [
"RuntimeStateEventWriter", "configure_writer", "build_plan", "write_event_with_state",
]
# 夹具层必须存在的函数式入口
LIVE_FIXTURE_SYMBOLS = [
"RuntimeEngine", "install", "uninstall", "apply", "read", "trace_kinds",
"update_statements", "insert_statements", "apply_runtime_event", "read_states",
"build_row", "assert_append_only", "RtxError", "percentile",
"EVENT_TABLE", "STATE_TABLE", "SNAPSHOT_TABLE",
]
_ORIGIN_DEFAULT_CLAUSE = compat._default_clause
def _default_clause_with_empty(fd):
dv = fd.get("default")
if isinstance(dv, str) and dv.strip() == "":
return " DEFAULT ''"
return _ORIGIN_DEFAULT_CLAUSE(fd)
def make_engine(path=":memory:"):
"""真 sqlite 事务引擎(表结构仍由夹具从 pbl_runtime_ext/models/*.json 生成)。"""
compat._default_clause = _default_clause_with_empty
try:
return compat.RuntimeEngine(path=path)
finally:
compat._default_clause = _ORIGIN_DEFAULT_CLAUSE
def su(entity="e1", state=None):
return {"entity_id": entity, "state": {"hp": 2} if state is None else state}
class FixtureSurfaceCase(unittest.TestCase):
"""夹具面:旧符号已死、新符号可直连真源。"""
def test_dead_class_style_symbols_absent(self):
for name in DEAD_CLASS_STYLE_SYMBOLS:
self.assertFalse(
hasattr(compat, name),
"夹具层不得复活旧类式符号 %s(第二条夹具路径 = M11b-2c 22 failed 根因)" % name)
def test_live_function_style_symbols_present(self):
for name in LIVE_FIXTURE_SYMBOLS:
self.assertTrue(hasattr(compat, name), "夹具缺少函数式符号 %s" % name)
self.assertTrue(callable(compat.install))
self.assertTrue(callable(compat.uninstall))
self.assertTrue(callable(compat.apply))
self.assertTrue(callable(compat.read))
def test_fixture_apply_is_thin_wrapper_on_true_source(self):
"""夹具 apply/read 不得自带实现——必须是真源的同步入口(零返回键增补)。"""
self.assertIs(compat.apply_runtime_event, apply_runtime_event)
src = inspect.getsource(compat.apply)
self.assertIn("apply_runtime_event", src)
self.assertNotIn("update(", src) # 不补键
self.assertNotIn("setdefault", src)
self.assertNotIn("_augment", src)
def test_true_source_signature(self):
"""真源逐参数签名钉死:形参名/顺序/默认值(文档 m11b2d-fixture-contract.md 同源)。"""
spec = inspect.signature(apply_runtime_event)
self.assertEqual(list(spec.parameters), TRUE_SOURCE_PARAMS)
for name in TRUE_SOURCE_PARAMS:
self.assertIsNone(spec.parameters[name].default,
"形参 %s 默认值应为 None" % name)
def test_read_states_signature(self):
spec = inspect.signature(read_states)
self.assertEqual(list(spec.parameters),
["tenant_id", "session_id", "entity_ids", "world_id", "env"])
class ReturnKeysCase(unittest.TestCase):
"""真源返回键齐全性(走夹具函数式路径,断言真值,不接受 print)。"""
def setUp(self):
self.eng = make_engine()
compat.install(self.eng)
self.addCleanup(compat.uninstall)
def _apply(self, **kw):
base = {"tenant_id": 7, "world_id": 1, "session_id": 11,
"event_type": "unit.move", "payload": {"dx": 1}}
base.update(kw)
return compat.apply(**base)
def test_success_return_keys_exact(self):
r = self._apply(state_updates=[su()], client_event_id="ev-1", actor_id="u1")
self.assertEqual(set(r.keys()), FULL_RETURN_KEYS)
self.assertIs(r["ok"], True)
self.assertIs(r["dedup"], False)
self.assertIsNotNone(r["event_id"])
self.assertEqual(r["seq"], 1)
self.assertEqual(r["seq_no"], r["seq"]) # 对外别名一致
self.assertEqual(r["state_rows"], 1)
self.assertEqual(r["channel"], "pbl:rtx:w1:t7")
self.assertIsInstance(r["stmt_count"], int)
self.assertGreaterEqual(r["stmt_count"], 2) # 事件 INSERT + 状态 UPSERT
self.assertIsInstance(r["states"], list)
self.assertEqual(set(r["states"][0].keys()),
{"entity_id", "state_version", "action", "state_key",
"state_value", "seq"})
self.assertEqual(r["states"][0]["action"], "insert")
self.assertEqual(r["states"][0]["state_version"], 1)
def test_dedup_return_keys_exact_and_no_second_write(self):
r1 = self._apply(state_updates=[su()], client_event_id="ev-1")
r2 = self._apply(state_updates=[su(state={"hp": 9})], client_event_id="ev-1")
self.assertEqual(set(r2.keys()), DEDUP_RETURN_KEYS)
self.assertIs(r2["dedup"], True)
self.assertEqual(r2["event_id"], r1["event_id"]) # 复用既有事件,不新写
self.assertEqual(r2["seq"], r1["seq"])
self.assertEqual(r2["state_rows"], 0)
self.assertIsNone(r2["latency_ms"])
self.assertEqual(self.eng.count(EVENT_TABLE), 1)
self.assertEqual(self.eng.count(STATE_TABLE), 1)
self.assertEqual(len(self.eng.broadcasts), 1) # 幂等命中不重复广播
def test_read_states_rows_shape(self):
self._apply(state_updates=[su()], client_event_id="ev-1")
rows = compat.read(tenant_id=7, session_id=11)
self.assertIsInstance(rows, list)
self.assertEqual(len(rows), 1)
row = rows[0]
self.assertEqual(row["entity_id"], "e1")
self.assertEqual(row["state"], {"hp": 2})
self.assertEqual(row["state_version"], 1)
self.assertIn("checksum", row)
self.assertIn("updated_at", row)
def test_snapshot_writes_snapshot_table(self):
r = self._apply(state_updates=[su(state={"hp": 5})], client_event_id="ev-4",
snapshot={"reason": "join", "hp": 5})
self.assertEqual(set(r.keys()), FULL_RETURN_KEYS)
self.assertEqual(self.eng.count(SNAPSHOT_TABLE), 1)
def test_broadcast_strictly_after_commit(self):
self._apply(state_updates=[su()], client_event_id="ev-1")
kinds = compat.trace_kinds(self.eng)
self.assertEqual(kinds[0], "BEGIN")
self.assertIn("COMMIT", kinds)
self.assertIn("BROADCAST", kinds)
self.assertGreater(kinds.index("BROADCAST"), kinds.index("COMMIT"),
"广播必须在事务提交之后(M11b-3 契约)")
self.assertEqual(self.eng.broadcasts[0]["channel"], "pbl:rtx:w1:t7")
self.assertEqual(self.eng.broadcasts[0]["event"]["event_type"], "unit.move")
def test_event_table_is_append_only(self):
"""事件表只允许 INSERT:UPDATE 语句里不得出现事件表名。"""
self._apply(state_updates=[su()], client_event_id="ev-1")
self._apply(state_updates=[su(state={"hp": 3})], client_event_id="ev-2")
for sql, _p in compat.update_statements(self.eng):
self.assertNotIn(EVENT_TABLE.upper(), sql.upper(),
"事件表出现 UPDATE 语句,违反 append-only:%s" % sql)
inserts = compat.insert_statements(self.eng, EVENT_TABLE)
self.assertEqual(len(inserts), 2)
for sql, _p in inserts:
self.assertIn(EVENT_TABLE.upper(), sql.upper())
class GateErrorCase(unittest.TestCase):
"""参数门禁与乐观锁冲突:必须抛 RtxError 且 code 精确、事务回滚不留脏行。"""
def setUp(self):
self.eng = make_engine()
compat.install(self.eng)
self.addCleanup(compat.uninstall)
def _expect(self, code, **kw):
base = {"tenant_id": 7, "world_id": 1, "session_id": 11,
"event_type": "x", "payload": {}}
base.update(kw)
with self.assertRaises(RtxError) as ctx:
compat.apply(**base)
self.assertEqual(ctx.exception.code, code)
return ctx.exception
def test_missing_tenant_is_fail_closed(self):
self._expect(ERR_TENANT_MISSING, tenant_id=None)
self.assertEqual(self.eng.count(EVENT_TABLE), 0)
def test_missing_required_params(self):
for key in ("world_id", "session_id", "event_type"):
self._expect(ERR_BAD_PARAM, **{key: None})
self.assertEqual(self.eng.count(EVENT_TABLE), 0)
def test_bad_state_updates(self):
self._expect(ERR_BAD_PARAM, state_updates="notalist")
self._expect(ERR_BAD_PARAM, state_updates=[{"state": {"a": 1}}]) # 缺 entity_id
self._expect(ERR_BAD_PARAM, state_updates=[{"entity_id": "e1"}]) # 缺 state
self.assertEqual(self.eng.count(STATE_TABLE), 0)
def test_state_conflict_rolls_back(self):
compat.apply(tenant_id=7, world_id=1, session_id=11, event_type="unit.move",
payload={}, state_updates=[su()], client_event_id="ev-1")
before_evt = self.eng.count(EVENT_TABLE)
before_state = self.eng.count(STATE_TABLE)
self._expect(ERR_STATE_CONFLICT, event_type="x",
state_updates=[su(state={"hp": 0})],
client_event_id="ev-3", base_version=999)
kinds = compat.trace_kinds(self.eng)
self.assertEqual(kinds[-1], "ROLLBACK", "冲突分支必须回滚,不得 COMMIT")
self.assertEqual(self.eng.count(EVENT_TABLE), before_evt)
self.assertEqual(self.eng.count(STATE_TABLE), before_state)
if __name__ == "__main__":
unittest.main(verbosity=2)