diff --git a/.m11b2c_tmp/pytest_run1.txt b/.m11b2c_tmp/pytest_run1.txt deleted file mode 100644 index f2c83ff..0000000 --- a/.m11b2c_tmp/pytest_run1.txt +++ /dev/null @@ -1,12 +0,0 @@ -=== CMD: .venv/bin/python -m pytest tests/ -v --tb=short === -=== CWD: /d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext === -=== DATE: 2026-09-21T11:23:05+08:00 === -============================= test session starts ============================== -platform linux -- Python 3.10.12, pytest-9.1.1, pluggy-1.6.0 -- /d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/.venv/bin/python -cachedir: .pytest_cache -rootdir: /d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext -configfile: pyproject.toml -collecting ... collected 0 items - -============================ no tests ran in 0.01s ============================= -=== EXIT_CODE=5 === diff --git a/.m11b2c_tmp/selftest.err b/.m11b2c_tmp/selftest.err deleted file mode 100644 index 3ea06f0..0000000 --- a/.m11b2c_tmp/selftest.err +++ /dev/null @@ -1,12 +0,0 @@ -Traceback (most recent call last): - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 555, in - sys.exit(main()) - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 542, in main - suite_memory() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 473, in suite_memory - asyncio.new_event_loop().run_until_complete(run()) - File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete - return future.result() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 299, in run - r1 = await wtx.apply_runtime_event( -TypeError: apply_runtime_event() got an unexpected keyword argument 'client_key' diff --git a/.m11b2c_tmp/selftest.out b/.m11b2c_tmp/selftest.out deleted file mode 100644 index 2bada90..0000000 --- a/.m11b2c_tmp/selftest.out +++ /dev/null @@ -1,68 +0,0 @@ - ---- A. 离线单元 --- -partitions | partitions PASS:跨年命名 ['p202611', 'p202612', PASS partitions PASS:跨年命名 ['p202611', 'p202612', 'p202701', 'p202702'] -partitions | partitions PASS:下月边界 2027-01-01 PASS partitions PASS:下月边界 2027-01-01 -partitions | partitions PASS:已存在分区 skip、缺失分区 add PASS partitions PASS:已存在分区 skip、缺失分区 add -partitions | partitions PASS:DDL ALTER TABLE pbl_runtime PASS partitions PASS:DDL ALTER TABLE pbl_runtime_event ADD PARTITION (PARTITION p202602 VALUES LESS THAN (DATE'2026-03-01')) -partitions | partitions.self_check PASS PASS partitions.self_check PASS -partitions.self_check 整体 PASS -broadcast | broadcast PASS:after_cursor=4 只回 1 条 PASS broadcast PASS:after_cursor=4 只回 1 条 -broadcast | broadcast PASS:环形缓冲溢出报 truncated=True PASS broadcast PASS:环形缓冲溢出报 truncated=True -broadcast | broadcast PASS:通道按 tenant.world 隔离 PASS broadcast PASS:通道按 tenant.world 隔离 -broadcast | broadcast PASS:订阅者扇出成功 PASS broadcast PASS:订阅者扇出成功 -broadcast | broadcast PASS:stats published=6 p95=0.016ms PASS broadcast PASS:stats published=6 p95=0.016ms 越界=0 -broadcast | broadcast PASS:通道命名 pbl.world.7.1 PASS broadcast PASS:通道命名 pbl.world.7.1 -broadcast | broadcast.self_check PASS PASS broadcast.self_check PASS -broadcast.self_check 整体 PASS -latency | latency PASS:裁决 120ms ≤ 200ms 未越界 PASS latency PASS:裁决 120ms ≤ 200ms 未越界 -latency | latency PASS:裁决 450ms > 200ms 记越界 PASS latency PASS:裁决 450ms > 200ms 记越界 -latency | latency PASS:summary avg=285.0 p95=450.0 breac PASS latency PASS:summary avg=285.0 p95=450.0 breaches=1 -latency | latency PASS:轮询兜底 3000ms 已配置 PASS latency PASS:轮询兜底 3000ms 已配置 -latency | latency PASS:p95=10 PASS latency PASS:p95=10 -latency | latency PASS:Timer 实测 5.1ms PASS latency PASS:Timer 实测 5.1ms -latency | latency.self_check PASS PASS latency.self_check PASS -latency.self_check 整体 PASS -tx_write | tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba41666 PASS tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba416666…) -tx_write | tx_write PASS:client_event_id 原样透传(重放同键) PASS tx_write PASS:client_event_id 原样透传(重放同键) -tx_write | tx_write PASS:pbl_runtime_event 禁 UPDATE/DELE PASS tx_write PASS:pbl_runtime_event 禁 UPDATE/DELETE/TRUNCATE -tx_write | tx_write PASS:基表写保护生效(world 等 6 张) PASS tx_write PASS:基表写保护生效(world 等 6 张) -tx_write | tx_write PASS:pbl_runtime_event 用到的 13 列与 mod PASS tx_write PASS:pbl_runtime_event 用到的 13 列与 models 定义一致 -tx_write | tx_write PASS:pbl_entity_state 用到的 9 列与 model PASS tx_write PASS:pbl_entity_state 用到的 9 列与 models 定义一致 -tx_write | tx_write PASS:pbl_world_state_snapshot 用到的 9 PASS tx_write PASS:pbl_world_state_snapshot 用到的 9 列与 models 定义一致 -tx_write | tx_write PASS:3 协程契约 + 1 同步守卫签名正确 PASS tx_write PASS:3 协程契约 + 1 同步守卫签名正确 -tx_write | probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件 PASS probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件行=1 状态行=2 快照行=1 广播在commit后=True 占位符违规=0 -tx_write | probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0( PASS probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0(重放不重复写、不重复广播) -tx_write | probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT PASS probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT commit=-1 rollback=5 事件行残留=0 广播=0 -tx_write | probe| PASS P4 中途故障原子性:抛出=RuntimeError rollba PASS probe| PASS P4 中途故障原子性:抛出=RuntimeError rollback=5 已提交事件行=0 已提交状态行=0 广播=0 -tx_write | probe| PASS P5 append-only:UPDATE/DELETE/TRUN PASS probe| PASS P5 append-only:UPDATE/DELETE/TRUNCATE 被代码守卫拦截=True 引擎兜底拦截=True -tx_write | probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒( PASS probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒(错误码=PBL-RTX-0005) -tx_write | probe| PASS P7 契约三处同步:6/6 对齐 PASS probe| PASS P7 契约三处同步:6/6 对齐 -tx_write | probe| m11b_probe PASS:7 项探针(state_updates 非空 PASS probe| m11b_probe PASS:7 项探针(state_updates 非空端到端 P1~P4 + 守卫 P5 + 门禁有效性 P6 + 三处同步 P7) -tx_write | tx_write.self_check PASS PASS tx_write.self_check PASS -tx_write.self_check 整体 PASS -m11b_api | m11b_api PASS:5 个协程契约齐备 PASS m11b_api PASS:5 个协程契约齐备 -m11b_api | m11b_api PASS:assert_append_only 同步守卫可调用 PASS m11b_api PASS:assert_append_only 同步守卫可调用 -m11b_api | m11b_api PASS:contracts 清单 8 项与注册表一致 PASS m11b_api PASS:contracts 清单 8 项与注册表一致 -m11b_api | m11b_api.self_check PASS PASS m11b_api.self_check PASS -m11b_api.self_check 整体 PASS -rtx_db | rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext PASS rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext(无模块级猜测签名) -rtx_db | rtx_db PASS:事务原语探测 sor/conn/sql 三级 PASS rtx_db PASS:事务原语探测 sor/conn/sql 三级 -rtx_db | rtx_db PASS:无 begin/commit/rollback → 探测返回 Non PASS rtx_db PASS:无 begin/commit/rollback → 探测返回 None(上层抛 PBL-RTX-0002) -rtx_db | rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) PASS rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) -rtx_db.self_check 整体 PASS -A[三处同步] apply_runtime_event PASS impl=True export=True register=True -A[三处同步] poll_events PASS impl=True export=True register=True -A[三处同步] read_states PASS impl=True export=True register=True -A[三处同步] assert_append_only PASS impl=True export=True register=True -A[三处同步] pbl_runtime_broadcast_pull PASS impl=True export=True register=True -A[三处同步] pbl_runtime_broadcast_stats PASS impl=True export=True register=True -A[包出口可导入] pbl_runtime_broadcast_pull PASS -A[包出口可导入] pbl_runtime_broadcast_stats PASS -A[包出口可导入] apply_runtime_event PASS -A[包出口可导入] poll_events PASS -A[包出口可导入] read_states PASS -A[包出口可导入] assert_append_only PASS -A[M11b 注册清单完整(fail-closed 门禁)] FAIL missing=['pbl_runtime_event_append', 'pbl_runtime_event_poll', 'pbl_entity_state_get', 'pbl_entity_state_apply', 'pbl_world_broadcast', 'pbl_session_member_add'] total=12 -A[M11a 注册·环境受限未验证(pbl_common 不可导入)] PASS missing=['pbl_runtime_event_append', 'pbl_runtime_event_poll', 'pbl_entity_state_get', 'pbl_entity_state_apply', 'pbl_world_broadcast', 'pbl_session_member_add'](真实宿主有 pbl_common,load 时 fail-closed 核对) - ---- B. 内存事务引擎端到端(真实 apply_runtime_event 主链路)--- diff --git a/.m11b2c_tmp/selftest2.err b/.m11b2c_tmp/selftest2.err deleted file mode 100644 index b3b1de7..0000000 --- a/.m11b2c_tmp/selftest2.err +++ /dev/null @@ -1,18 +0,0 @@ -Traceback (most recent call last): - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 555, in - sys.exit(main()) - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 542, in main - suite_memory() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 473, in suite_memory - asyncio.new_event_loop().run_until_complete(run()) - File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete - return future.result() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 389, in run - await wtx.apply_runtime_event( - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 338, in apply_runtime_event - st = await _apply_state(tx, tenant_id, session_id, upd, seq_no, - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 253, in _apply_state - vals = build_row(STATE_TABLE, { - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 128, in build_row - raise rtx_db.RtxError("PBL-RTX-0004", -pbl_runtime_ext.rtx_db.RtxError: [PBL-RTX-0004] pbl_entity_state 缺少 NOT NULL 列 ['tenant_id', 'session_id', 'entity_id', 'created_at'] diff --git a/.m11b2c_tmp/selftest2.out b/.m11b2c_tmp/selftest2.out deleted file mode 100644 index d25cb9f..0000000 --- a/.m11b2c_tmp/selftest2.out +++ /dev/null @@ -1,89 +0,0 @@ - ---- A. 离线单元 --- -partitions | partitions PASS:跨年命名 ['p202611', 'p202612', PASS partitions PASS:跨年命名 ['p202611', 'p202612', 'p202701', 'p202702'] -partitions | partitions PASS:下月边界 2027-01-01 PASS partitions PASS:下月边界 2027-01-01 -partitions | partitions PASS:已存在分区 skip、缺失分区 add PASS partitions PASS:已存在分区 skip、缺失分区 add -partitions | partitions PASS:DDL ALTER TABLE pbl_runtime PASS partitions PASS:DDL ALTER TABLE pbl_runtime_event ADD PARTITION (PARTITION p202602 VALUES LESS THAN (DATE'2026-03-01')) -partitions | partitions.self_check PASS PASS partitions.self_check PASS -partitions.self_check 整体 PASS -broadcast | broadcast PASS:after_cursor=4 只回 1 条 PASS broadcast PASS:after_cursor=4 只回 1 条 -broadcast | broadcast PASS:环形缓冲溢出报 truncated=True PASS broadcast PASS:环形缓冲溢出报 truncated=True -broadcast | broadcast PASS:通道按 tenant.world 隔离 PASS broadcast PASS:通道按 tenant.world 隔离 -broadcast | broadcast PASS:订阅者扇出成功 PASS broadcast PASS:订阅者扇出成功 -broadcast | broadcast PASS:stats published=6 p95=0.018ms PASS broadcast PASS:stats published=6 p95=0.018ms 越界=0 -broadcast | broadcast PASS:通道命名 pbl.world.7.1 PASS broadcast PASS:通道命名 pbl.world.7.1 -broadcast | broadcast.self_check PASS PASS broadcast.self_check PASS -broadcast.self_check 整体 PASS -latency | latency PASS:裁决 120ms ≤ 200ms 未越界 PASS latency PASS:裁决 120ms ≤ 200ms 未越界 -latency | latency PASS:裁决 450ms > 200ms 记越界 PASS latency PASS:裁决 450ms > 200ms 记越界 -latency | latency PASS:summary avg=285.0 p95=450.0 breac PASS latency PASS:summary avg=285.0 p95=450.0 breaches=1 -latency | latency PASS:轮询兜底 3000ms 已配置 PASS latency PASS:轮询兜底 3000ms 已配置 -latency | latency PASS:p95=10 PASS latency PASS:p95=10 -latency | latency PASS:Timer 实测 5.1ms PASS latency PASS:Timer 实测 5.1ms -latency | latency.self_check PASS PASS latency.self_check PASS -latency.self_check 整体 PASS -tx_write | tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba41666 PASS tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba416666…) -tx_write | tx_write PASS:client_event_id 原样透传(重放同键) PASS tx_write PASS:client_event_id 原样透传(重放同键) -tx_write | tx_write PASS:pbl_runtime_event 禁 UPDATE/DELE PASS tx_write PASS:pbl_runtime_event 禁 UPDATE/DELETE/TRUNCATE -tx_write | tx_write PASS:基表写保护生效(world 等 6 张) PASS tx_write PASS:基表写保护生效(world 等 6 张) -tx_write | tx_write PASS:pbl_runtime_event 用到的 13 列与 mod PASS tx_write PASS:pbl_runtime_event 用到的 13 列与 models 定义一致 -tx_write | tx_write PASS:pbl_entity_state 用到的 9 列与 model PASS tx_write PASS:pbl_entity_state 用到的 9 列与 models 定义一致 -tx_write | tx_write PASS:pbl_world_state_snapshot 用到的 9 PASS tx_write PASS:pbl_world_state_snapshot 用到的 9 列与 models 定义一致 -tx_write | tx_write PASS:3 协程契约 + 1 同步守卫签名正确 PASS tx_write PASS:3 协程契约 + 1 同步守卫签名正确 -tx_write | probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件 PASS probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件行=1 状态行=2 快照行=1 广播在commit后=True 占位符违规=0 -tx_write | probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0( PASS probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0(重放不重复写、不重复广播) -tx_write | probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT PASS probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT commit=-1 rollback=5 事件行残留=0 广播=0 -tx_write | probe| PASS P4 中途故障原子性:抛出=RuntimeError rollba PASS probe| PASS P4 中途故障原子性:抛出=RuntimeError rollback=5 已提交事件行=0 已提交状态行=0 广播=0 -tx_write | probe| PASS P5 append-only:UPDATE/DELETE/TRUN PASS probe| PASS P5 append-only:UPDATE/DELETE/TRUNCATE 被代码守卫拦截=True 引擎兜底拦截=True -tx_write | probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒( PASS probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒(错误码=PBL-RTX-0005) -tx_write | probe| PASS P7 契约三处同步:6/6 对齐 PASS probe| PASS P7 契约三处同步:6/6 对齐 -tx_write | probe| m11b_probe PASS:7 项探针(state_updates 非空 PASS probe| m11b_probe PASS:7 项探针(state_updates 非空端到端 P1~P4 + 守卫 P5 + 门禁有效性 P6 + 三处同步 P7) -tx_write | tx_write.self_check PASS PASS tx_write.self_check PASS -tx_write.self_check 整体 PASS -m11b_api | m11b_api PASS:5 个协程契约齐备 PASS m11b_api PASS:5 个协程契约齐备 -m11b_api | m11b_api PASS:assert_append_only 同步守卫可调用 PASS m11b_api PASS:assert_append_only 同步守卫可调用 -m11b_api | m11b_api PASS:contracts 清单 8 项与注册表一致 PASS m11b_api PASS:contracts 清单 8 项与注册表一致 -m11b_api | m11b_api.self_check PASS PASS m11b_api.self_check PASS -m11b_api.self_check 整体 PASS -rtx_db | rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext PASS rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext(无模块级猜测签名) -rtx_db | rtx_db PASS:事务原语探测 sor/conn/sql 三级 PASS rtx_db PASS:事务原语探测 sor/conn/sql 三级 -rtx_db | rtx_db PASS:无 begin/commit/rollback → 探测返回 Non PASS rtx_db PASS:无 begin/commit/rollback → 探测返回 None(上层抛 PBL-RTX-0002) -rtx_db | rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) PASS rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) -rtx_db.self_check 整体 PASS -A[三处同步] apply_runtime_event PASS impl=True export=True register=True -A[三处同步] poll_events PASS impl=True export=True register=True -A[三处同步] read_states PASS impl=True export=True register=True -A[三处同步] assert_append_only PASS impl=True export=True register=True -A[三处同步] pbl_runtime_broadcast_pull PASS impl=True export=True register=True -A[三处同步] pbl_runtime_broadcast_stats PASS impl=True export=True register=True -A[包出口可导入] pbl_runtime_broadcast_pull PASS -A[包出口可导入] pbl_runtime_broadcast_stats PASS -A[包出口可导入] apply_runtime_event PASS -A[包出口可导入] poll_events PASS -A[包出口可导入] read_states PASS -A[包出口可导入] assert_append_only PASS -A[M11b 注册清单完整(fail-closed 门禁)] PASS missing=[] total=12 -A[M11a 注册清单完整] PASS total=6 - ---- B. 内存事务引擎端到端(真实 apply_runtime_event 主链路)--- -B1[正常写入] ok 且非幂等命中 PASS seq_no=1 event_id=1 -B1[正常写入] 事件表 1 行 PASS -B1[正常写入] 状态表 1 行 v1 PASS -B1[正常写入] 快照表 1 行 PASS -B1[正常写入] 单事务多语句 PASS stmt_count=3 -B1[正常写入] 提交后广播进缓冲 PASS cursor=1 -B2[无幽灵更新] 事务日志 begin→commit PASS log=['begin', 'commit'] -B2[无幽灵更新] commit 时 hub 游标=0,广播后=1 PASS commit_cursor=0 now=1 -B3[幂等重放] dedup=True PASS event_id=1 -B3[幂等重放] 事件仍 1 行 PASS -B3[幂等重放] 未重复广播 PASS cursor 1→1 -B4[乐观锁冲突] PBL-STATE-CONFLICT PASS entity_id=pos 乐观锁冲突:base_version=99 当前 state_version=1(整 -B4[乐观锁冲突] 事件行数未增加(无脏数据) PASS 1→1 -B4[乐观锁冲突] 冲突键未落库 PASS -B4[乐观锁冲突] 状态版本未污染(仍 v1) PASS -B4[乐观锁冲突] 未广播 PASS -B5[DML失败] 异常上抛(不静默) PASS 注入故障:pbl_entity_state INSERT 失败 -B5[DML失败] 事件行不存在(回滚证据,非标记 rolled_back) PASS events=1 -B5[DML失败] 无状态脏行 PASS -B5[DML失败] 未广播(无幽灵更新) PASS -B6[回滚证据] 日志含 rollback PASS log=['begin', 'commit', 'begin', 'commit', 'begin', 'rollback', 'begin', 'rollback'] diff --git a/.m11b2c_tmp/selftest_run1.txt b/.m11b2c_tmp/selftest_run1.txt deleted file mode 100644 index c627bcd..0000000 --- a/.m11b2c_tmp/selftest_run1.txt +++ /dev/null @@ -1,110 +0,0 @@ -=== CMD: .venv/bin/python scripts/m11b_selftest.py === -=== DATE: 2026-09-21T11:23:30+08:00 === - ---- A. 离线单元 --- -partitions | partitions PASS:跨年命名 ['p202611', 'p202612', PASS partitions PASS:跨年命名 ['p202611', 'p202612', 'p202701', 'p202702'] -partitions | partitions PASS:下月边界 2027-01-01 PASS partitions PASS:下月边界 2027-01-01 -partitions | partitions PASS:已存在分区 skip、缺失分区 add PASS partitions PASS:已存在分区 skip、缺失分区 add -partitions | partitions PASS:DDL ALTER TABLE pbl_runtime PASS partitions PASS:DDL ALTER TABLE pbl_runtime_event ADD PARTITION (PARTITION p202602 VALUES LESS THAN (DATE'2026-03-01')) -partitions | partitions.self_check PASS PASS partitions.self_check PASS -partitions.self_check 整体 PASS -broadcast | broadcast PASS:after_cursor=4 只回 1 条 PASS broadcast PASS:after_cursor=4 只回 1 条 -broadcast | broadcast PASS:环形缓冲溢出报 truncated=True PASS broadcast PASS:环形缓冲溢出报 truncated=True -broadcast | broadcast PASS:通道按 tenant.world 隔离 PASS broadcast PASS:通道按 tenant.world 隔离 -broadcast | broadcast PASS:订阅者扇出成功 PASS broadcast PASS:订阅者扇出成功 -broadcast | broadcast PASS:stats published=6 p95=0.018ms PASS broadcast PASS:stats published=6 p95=0.018ms 越界=0 -broadcast | broadcast PASS:通道命名 pbl.world.7.1 PASS broadcast PASS:通道命名 pbl.world.7.1 -broadcast | broadcast.self_check PASS PASS broadcast.self_check PASS -broadcast.self_check 整体 PASS -latency | latency PASS:裁决 120ms ≤ 200ms 未越界 PASS latency PASS:裁决 120ms ≤ 200ms 未越界 -latency | latency PASS:裁决 450ms > 200ms 记越界 PASS latency PASS:裁决 450ms > 200ms 记越界 -latency | latency PASS:summary avg=285.0 p95=450.0 breac PASS latency PASS:summary avg=285.0 p95=450.0 breaches=1 -latency | latency PASS:轮询兜底 3000ms 已配置 PASS latency PASS:轮询兜底 3000ms 已配置 -latency | latency PASS:p95=10 PASS latency PASS:p95=10 -latency | latency PASS:Timer 实测 5.1ms PASS latency PASS:Timer 实测 5.1ms -latency | latency.self_check PASS PASS latency.self_check PASS -latency.self_check 整体 PASS -tx_write | tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba41666 PASS tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba416666…) -tx_write | tx_write PASS:client_event_id 原样透传(重放同键) PASS tx_write PASS:client_event_id 原样透传(重放同键) -tx_write | tx_write PASS:pbl_runtime_event 禁 UPDATE/DELE PASS tx_write PASS:pbl_runtime_event 禁 UPDATE/DELETE/TRUNCATE -tx_write | tx_write PASS:基表写保护生效(world 等 6 张) PASS tx_write PASS:基表写保护生效(world 等 6 张) -tx_write | tx_write PASS:pbl_runtime_event 用到的 13 列与 mod PASS tx_write PASS:pbl_runtime_event 用到的 13 列与 models 定义一致 -tx_write | tx_write PASS:pbl_entity_state 用到的 9 列与 model PASS tx_write PASS:pbl_entity_state 用到的 9 列与 models 定义一致 -tx_write | tx_write PASS:pbl_world_state_snapshot 用到的 9 PASS tx_write PASS:pbl_world_state_snapshot 用到的 9 列与 models 定义一致 -tx_write | tx_write PASS:3 协程契约 + 1 同步守卫签名正确 PASS tx_write PASS:3 协程契约 + 1 同步守卫签名正确 -tx_write | probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件 PASS probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件行=1 状态行=2 快照行=1 广播在commit后=True 占位符违规=0 -tx_write | probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0( PASS probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0(重放不重复写、不重复广播) -tx_write | probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT PASS probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT commit=-1 rollback=5 事件行残留=0 广播=0 -tx_write | probe| PASS P4 中途故障原子性:抛出=RuntimeError rollba PASS probe| PASS P4 中途故障原子性:抛出=RuntimeError rollback=5 已提交事件行=0 已提交状态行=0 广播=0 -tx_write | probe| PASS P5 append-only:UPDATE/DELETE/TRUN PASS probe| PASS P5 append-only:UPDATE/DELETE/TRUNCATE 被代码守卫拦截=True 引擎兜底拦截=True -tx_write | probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒( PASS probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒(错误码=PBL-RTX-0005) -tx_write | probe| PASS P7 契约三处同步:6/6 对齐 PASS probe| PASS P7 契约三处同步:6/6 对齐 -tx_write | probe| m11b_probe PASS:7 项探针(state_updates 非空 PASS probe| m11b_probe PASS:7 项探针(state_updates 非空端到端 P1~P4 + 守卫 P5 + 门禁有效性 P6 + 三处同步 P7) -tx_write | tx_write.self_check PASS PASS tx_write.self_check PASS -tx_write.self_check 整体 PASS -m11b_api | m11b_api PASS:5 个协程契约齐备 PASS m11b_api PASS:5 个协程契约齐备 -m11b_api | m11b_api PASS:assert_append_only 同步守卫可调用 PASS m11b_api PASS:assert_append_only 同步守卫可调用 -m11b_api | m11b_api PASS:contracts 清单 8 项与注册表一致 PASS m11b_api PASS:contracts 清单 8 项与注册表一致 -m11b_api | m11b_api.self_check PASS PASS m11b_api.self_check PASS -m11b_api.self_check 整体 PASS -rtx_db | rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext PASS rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext(无模块级猜测签名) -rtx_db | rtx_db PASS:事务原语探测 sor/conn/sql 三级 PASS rtx_db PASS:事务原语探测 sor/conn/sql 三级 -rtx_db | rtx_db PASS:无 begin/commit/rollback → 探测返回 Non PASS rtx_db PASS:无 begin/commit/rollback → 探测返回 None(上层抛 PBL-RTX-0002) -rtx_db | rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) PASS rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) -rtx_db.self_check 整体 PASS -A[三处同步] apply_runtime_event PASS impl=True export=True register=True -A[三处同步] poll_events PASS impl=True export=True register=True -A[三处同步] read_states PASS impl=True export=True register=True -A[三处同步] assert_append_only PASS impl=True export=True register=True -A[三处同步] pbl_runtime_broadcast_pull PASS impl=True export=True register=True -A[三处同步] pbl_runtime_broadcast_stats PASS impl=True export=True register=True -A[包出口可导入] pbl_runtime_broadcast_pull PASS -A[包出口可导入] pbl_runtime_broadcast_stats PASS -A[包出口可导入] apply_runtime_event PASS -A[包出口可导入] poll_events PASS -A[包出口可导入] read_states PASS -A[包出口可导入] assert_append_only PASS -A[M11b 注册清单完整(fail-closed 门禁)] PASS missing=[] total=12 -A[M11a 注册清单完整] PASS total=6 - ---- B. 内存事务引擎端到端(真实 apply_runtime_event 主链路)--- -B1[正常写入] ok 且非幂等命中 PASS seq_no=1 event_id=1 -B1[正常写入] 事件表 1 行 PASS -B1[正常写入] 状态表 1 行 v1 PASS -B1[正常写入] 快照表 1 行 PASS -B1[正常写入] 单事务多语句 PASS stmt_count=3 -B1[正常写入] 提交后广播进缓冲 PASS cursor=1 -B2[无幽灵更新] 事务日志 begin→commit PASS log=['begin', 'commit'] -B2[无幽灵更新] commit 时 hub 游标=0,广播后=1 PASS commit_cursor=0 now=1 -B3[幂等重放] dedup=True PASS event_id=1 -B3[幂等重放] 事件仍 1 行 PASS -B3[幂等重放] 未重复广播 PASS cursor 1→1 -B4[乐观锁冲突] PBL-STATE-CONFLICT PASS entity_id=pos 乐观锁冲突:base_version=99 当前 state_version=1(整 -B4[乐观锁冲突] 事件行数未增加(无脏数据) PASS 1→1 -B4[乐观锁冲突] 冲突键未落库 PASS -B4[乐观锁冲突] 状态版本未污染(仍 v1) PASS -B4[乐观锁冲突] 未广播 PASS -B5[DML失败] 异常上抛(不静默) PASS 注入故障:pbl_entity_state INSERT 失败 -B5[DML失败] 事件行不存在(回滚证据,非标记 rolled_back) PASS events=1 -B5[DML失败] 无状态脏行 PASS -B5[DML失败] 未广播(无幽灵更新) PASS -B6[回滚证据] 日志含 rollback PASS log=['begin', 'commit', 'begin', 'commit', 'begin', 'rollback', 'begin', 'rollback'] -Traceback (most recent call last): - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 555, in - sys.exit(main()) - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 542, in main - suite_memory() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 473, in suite_memory - asyncio.new_event_loop().run_until_complete(run()) - File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete - return future.result() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 389, in run - await wtx.apply_runtime_event( - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 338, in apply_runtime_event - st = await _apply_state(tx, tenant_id, session_id, upd, seq_no, - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 253, in _apply_state - vals = build_row(STATE_TABLE, { - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 128, in build_row - raise rtx_db.RtxError("PBL-RTX-0004", -pbl_runtime_ext.rtx_db.RtxError: [PBL-RTX-0004] pbl_entity_state 缺少 NOT NULL 列 ['tenant_id', 'session_id', 'entity_id', 'created_at'] -=== EXIT_CODE=1 === diff --git a/.m11b2c_tmp/tb3.err b/.m11b2c_tmp/tb3.err deleted file mode 100644 index b3b1de7..0000000 --- a/.m11b2c_tmp/tb3.err +++ /dev/null @@ -1,18 +0,0 @@ -Traceback (most recent call last): - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 555, in - sys.exit(main()) - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 542, in main - suite_memory() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 473, in suite_memory - asyncio.new_event_loop().run_until_complete(run()) - File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete - return future.result() - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/scripts/m11b_selftest.py", line 389, in run - await wtx.apply_runtime_event( - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 338, in apply_runtime_event - st = await _apply_state(tx, tenant_id, session_id, upd, seq_no, - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 253, in _apply_state - vals = build_row(STATE_TABLE, { - File "/d/pipeline/workspaces/0/sdlc_general/modules/pbl_runtime_ext/pbl_runtime_ext/tx_write.py", line 128, in build_row - raise rtx_db.RtxError("PBL-RTX-0004", -pbl_runtime_ext.rtx_db.RtxError: [PBL-RTX-0004] pbl_entity_state 缺少 NOT NULL 列 ['tenant_id', 'session_id', 'entity_id', 'created_at'] diff --git a/docs/work-log-2026-09-22.md b/docs/work-log-2026-09-22.md new file mode 100644 index 0000000..41bdfd3 --- /dev/null +++ b/docs/work-log-2026-09-22.md @@ -0,0 +1,58 @@ +# Work Log — 2026-09-22(M11b-2c-A 实跑 pytest + selftest 并落盘留证 / QC 退回响应) + +- **模块仓库**: `modules/pbl_runtime_ext`(PBL scense_runtime 薄扩展,M11a/M11b 单事务事件+状态写入与广播) +- **任务**: [M11b-2c-A] 实跑 pytest + selftest 并落盘留证(QC 退回 5 条意见逐条响应) +- **执行环境**: Python 3.10.12 / pytest 9.1.1 / venv=`.venv`(已存在,`pip install -e .` 已装) +- **起始 HEAD**: `51c3f26`(含 `5542cf5` PBL-RTX-0004 修复) + +## 1. 本轮时间线(命令与结果) + +| # | 命令 | 结果 | 退出码 | +|---|------|------|--------| +| 1 | `.venv/bin/python -m pytest tests/ -v --tb=short` | `9 passed in 0.40s`(collected 9 items) | 0 | +| 2 | `.venv/bin/python scripts/m11b_selftest.py` | `总计 110 项,PASS 110,FAIL 0` / `RESULT: PASS` | 0 | +| 3 | `.venv/bin/python scripts/m11b2c_p99_probe.py --samples 30` | write(INSERT) P99=0.1946ms / read P99=0.1238ms / write(UPDATE) P99=0.4233ms,`P99 结论:达标` | 0 | +| 4 | `bash tests/run_m11b1a_localdb.sh` | **失败**(实库 DDL 装载 ERROR 1064,见第 4 节) | 1 | + +留证落点(project-directory-spec 第五节 develop 落点,非模块仓库内临时目录): + +- `projects/pbls/docs/02-develop/m11b2c-pytest-run.log` — 本轮重跑,含完整命令 + 时间戳 + `9 passed` + `EXIT_CODE=0` +- `projects/pbls/docs/02-develop/m11b2c-selftest-run.log` — selftest 110 项 + P99 数值行 + 实库 runner 原样输出与根因 +- `projects/pbls/docs/02-develop/m11b2c-pytest-run-history-20260921T1150-failed.log` — 上一轮 `3 failed, 6 passed` 失败证据**归档保留**(未覆盖、未删改) + +## 2. QC 退回意见逐条响应 + +| QC | 问题 | 本轮处置 | 证据 | +|----|------|----------|------| +| #1 | T3 因 PBL-RTX-0004 失败(`_apply_state` UPDATE 分支 NOT NULL 全列构造) | 采纳 (b) 路线:该缺陷已在前序 commit `5542cf5` 修复(`build_row(full_row=False)` UPDATE 语义 + `required_columns` 识别 nullable/排除自增与 DEFAULT 列 + self_check 闭环门禁)。本轮实跑 T3 由 FAIL → PASS,**未使用 xfail / 未放宽断言阈值 / 未静默跳过** | pytest 日志 T3 PASSED;selftest B7[顺序与版本] 全 PASS | +| #2 | 仓库唯一留证是失败证据(`collected 0 items / EXIT_CODE=5`、`EXIT_CODE=1`),与「实跑留证」表述矛盾 | 重跑并覆盖到规范落点,两份 log 均含完整命令行、时间戳、`9 passed`、`EXIT_CODE=0`,并补 `m11b2c_p99_probe.py` 成功输出(P99 数值行 + `P99 结论:达标`);旧失败证据改名归档不丢失 | 上述三个 log 文件 | +| #3 | 探针 `assert len(rows) == 1` 恒不成立(预热 state_key 与采样同 (tenant,session),`entity_ids` 在内存替身未下推)→ T4/T4b 崩溃、P99 从未产出 | 预热改用独立 `session_id=90911`;采样侧按 `entity_id` 二次过滤后断言;新增「返回行 entity_id 与请求一致」断言(`bad_rows` 计数并入 verdict)防再次静默放宽 | 探针输出 `read 行 entity_id 一致性校验:PASS 全部与请求一致`;T4/T4b/T4c 全 PASS | +| #4 | `.m11b2c_tmp/` 5 个 `.out/.err` 临时产物被 git 收口提交进模块仓库,污染仓库且不在规范落点 | `git rm -r --cached .m11b2c_tmp/` + 物理删除目录;`.gitignore` 已含 `.m11b2c_tmp/`、`.pytest_cache/`、`__pycache__/`、`.venv/`、`*.log`;留证迁至 `projects/pbls/docs/02-develop/`;本 work-log 按 module-development-spec 补齐 | `git status --short` 中 7 条 `D .m11b2c_tmp/*` | +| #5 | `test_t4c_probe_declares_environment_limit_honestly` 是 `open().read()` 字符串包含式 grep 伪测试(T4/T4b 全崩时它仍 PASS) | 重写为 `test_t4c_probe_runs_and_discloses_environment_limit`:真实调用 `p99_probe.run(30)`,断言 `rc in (0,1)`(rc=2 判为探针失效)、`rw['n']>=30`、`rr['n']>=30`、六项百分位元数据非 NaN、`P99<=200ms`、capsys 捕获输出中确有 `P99=` 数值行与 `P99 结论:` 行、entity_id 一致性校验 PASS;「环境受限诚实声明」降级为对模块 docstring 的**附加**校验而非唯一断言 | `tests/test_m11b2c_runtime_ext.py::test_t4c_probe_runs_and_discloses_environment_limit` PASSED | + +## 3. 关键技术决策与陷阱 + +1. **不放宽断言,改修根因**:QC #1 给了 xfail 挂单与真修两条路。因 PBL-RTX-0004 属本模块 `_apply_state` UPDATE 分支的实现缺陷(非外部业务缺陷),且修复已落地,选真修路线——留证必须是「全绿 + RC=0」的真实执行,不接受用 xfail 掩盖可修缺陷。 +2. **失败证据不删除,只归档**:QC #2 的实质是「声称与实测矛盾」,处置方式是补出成功证据,同时把失败证据改名为 `*-history--failed.log` 保留,形成缺陷→修复→复验的完整链条。 +3. **P99 采样的分支覆盖**:探针 S 段(唯一 entity_id → 状态行首建 INSERT 分支)与 U 段(固定 entity_id 反复覆写 → UPDATE 分支,正是 PBL-RTX-0004 触发点)分别计时,避免修复后 UPDATE 分支重新劣化无人察觉。U 段 P99=0.42ms 与 S 段同量级,说明修复未引入性能回退。 +4. **诚实声明(环境受限)**:内存事务引擎 FakeEngine 驱动的 P99 覆盖「幂等键计算 → 事件 INSERT → 状态 UPSERT(乐观锁) → 快照 INSERT → commit → 提交后广播」完整代码路径,但**不含网络 RTT 与真实 InnoDB 提交开销**,不冒充真实库数字。真实库复测路径:pbls 应用环境执行 `python3 scripts/m11b_selftest.py --live`。 +5. **陷阱**:`read_states` 按 `(tenant_id, session_id)` 过滤,`entity_ids` 入参在内存替身实现中不下推 IN 条件——任何按 entity_id 断言行数的写法都必须自己二次过滤,否则断言恒假(本轮 QC #3 根因)。 + +## 4. 环境受限项 / 遗留缺陷(上报 PM) + +- **C 层真实库端到端未验证**:本工作空间无 ahserver/apppublic/sqlor 运行时(`ModuleNotFoundError`),selftest C 段自身即 SKIP 并如实声明。 +- **`bash tests/run_m11b1a_localdb.sh` 失败(新发现,非本轮引入)**:一次性本地 MariaDB 能正常拉起(`mariadbd ready (2s)`),但 `scripts/pbl_runtime_event_ddl.py:full_script()` 渲染的 C 段存储过程 `pbl_assert_append_only` 中 `IF ... THEN RETURN; END IF;` 未被 DELIMITER 正确包裹,mariadb CLI 在 `RETURN` 后的分号提前切分语句 → + `ERROR 1064 (42000) at line 65` → 后续 `CALL pbl_runtime_event_ensure_partitions(9)` 报 `1305 PROCEDURE pbls.pbl_runtime_event_ensure_partitions does not exist` → runner `rc=1`。 + - 建议登记为 **PBL-RTX-0005**(DDL 生成器存储过程 DELIMITER 兼容性缺陷,影响 append-only 守卫与分区维护过程实库装载)。 + - 本任务边界禁止修改 `scripts/pbl_runtime_event_ddl.py` 与 `models/*.json`,故未在此修复;原样输出与根因已写入 `m11b2c-selftest-run.log` 第 [3/3] 段。 + - 等价覆盖已按任务规则补记:A 层离线核验 18 项 → pytest T1/T1b;B 层端到端 110 项 → pytest T3(同一份 `tx_write` 主链路代码)。 +- **develop 侧无 report_bug 工具**:本轮按角色工具集(classify/start_fix/fix_bug/list_bugs)无立单能力,PBL-RTX-0005 以 work-log + 交付摘要 + log 三处留痕方式上报 PM,请 PM 派生修复任务。 + +## 5. 分支与提交状态 + +- 分支:`main`(本地仓库,无远程;部署前需 `git remote add origin` 才能被 deploy 角色 pull) +- 本轮工作区变更(本 log 落笔时 `git status --short`): + - `D .m11b2c_tmp/pytest_run1.txt`、`D .m11b2c_tmp/selftest.err`、`D .m11b2c_tmp/selftest.out`、`D .m11b2c_tmp/selftest2.err`、`D .m11b2c_tmp/selftest2.out`、`D .m11b2c_tmp/selftest_run1.txt`、`D .m11b2c_tmp/tb3.err`(索引内删除,收口时生效) + - `M tests/test_m11b2c_runtime_ext.py`(T4c 重写为行为断言 + T3b read 行按 entity_id 过滤) + - `?? docs/work-log-2026-09-22.md`(本文件) +- 按任务边界,git commit 由 PM 审核通过后系统统一收口(引擎代为执行),本角色不自称已 commit/push。 diff --git a/tests/test_m11b2c_runtime_ext.py b/tests/test_m11b2c_runtime_ext.py index d32b0e2..c5344e5 100644 --- a/tests/test_m11b2c_runtime_ext.py +++ b/tests/test_m11b2c_runtime_ext.py @@ -13,6 +13,9 @@ (幂等 / 乐观锁 / 回滚无脏数据 / 提交后广播 / seq 单调 / SLA 打点 / 分区降级) T4 scripts/m11b2c_p99_probe.py:write_event_with_state(→apply_runtime_event) 与 read_entity_state(→read_states) 的 P99 实测(≥30 次采样,门禁 ≤200ms) + T4c 同一探针的 **行为断言**(QC #5 修正):真实调用 run() 校验返回的 rc 与 + 采样计数/百分位元数据,并把「环境受限」如实声明作为对模块元数据(docstring) + 的校验 —— 不再用 open().read() 字符串包含式 grep 凑数。 边界:只测不改业务实现、不改 models/*.json 表定义。 运行:.venv/bin/python -m pytest tests/ -v --tb=short @@ -157,6 +160,7 @@ def test_t3b_state_conflict_rolls_back_cleanly(selftest): assert len(engine.committed_rows("pbl_runtime_event")) == n_ev assert hub.latest_cursor() == cur rows = await wtx.read_states(tenant_id=7, session_id=21, entity_ids=["pos"]) + rows = [x for x in rows if str(x.get("entity_id")) == "pos"] assert len(rows) == 1 and int(rows[0]["state_version"]) == 1 asyncio.new_event_loop().run_until_complete(run()) @@ -180,8 +184,38 @@ def test_t4b_p99_read_entity_state_within_sla(p99_probe): "read_entity_state P99=%.4fms 超 200ms 门禁" % rr["p99_ms"]) -def test_t4c_probe_declares_environment_limit_honestly(p99_probe, capsys): - """探针必须如实声明:内存引擎 P99 不含真实库 RTT/InnoDB 提交开销。""" - text = open(os.path.join(SCRIPTS, "m11b2c_p99_probe.py"), encoding="utf-8").read() - assert "不含网络 RTT" in text or "真实库" in text, "探针缺少环境受限的诚实声明" - assert "--live" in text, "探针未指明真实库复测路径" +def test_t4c_probe_runs_and_discloses_environment_limit(p99_probe, capsys): + """QC #5 修正:行为断言取代 grep 式字符串包含检查。 + + 历史版本本用例只做 `open(probe).read()` 的字符串包含(grep 伪测试),不执行任何 + 被测行为 —— 反证是 T4/T4b 全崩(探针 len(rows)==1 断言失败)时 T4c 仍 PASS。 + 现在真实调用 p99_probe.run(),断言: + · 退出码 rc ∈ {0,1}(0=达标,1=超门禁但如实判定;2=采样不足属探针缺陷); + · write/read 两项样本数 n ≥ 30,且百分位元数据齐全(非 NaN、非 None); + · 探针真实打印了 P99 数值行与结论行(从 capsys 输出中校验,属执行结果校验); + · read 行 entity_id 一致性校验为 PASS(QC #3 的防静默放宽门禁落到断言上)。 + 「环境受限的诚实声明」改为对模块元数据(docstring)的校验,作为附加项而非唯一断言。 + """ + capsys.readouterr() # 清掉 T4/T4b 的缓冲输出,只取本次 + rc, rw, rr = p99_probe.run(P99_MIN_SAMPLES) + out = capsys.readouterr().out + + # 1) 探针必须真的跑完并给出可判定结论(rc=2 表示采样不足 = 探针自身失效) + assert rc in (0, 1), "探针退出码异常 rc=%s(2=采样不足,探针未产出有效 P99)" % rc + # 2) 样本数与百分位元数据 + assert rw["n"] >= P99_MIN_SAMPLES, "write 样本数 %d < %d" % (rw["n"], P99_MIN_SAMPLES) + assert rr["n"] >= P99_MIN_SAMPLES, "read 样本数 %d < %d" % (rr["n"], P99_MIN_SAMPLES) + for key in ("min_ms", "avg_ms", "p50_ms", "p95_ms", "p99_ms", "max_ms"): + assert isinstance(rw.get(key), float) and rw[key] == rw[key], "write %s 缺失/NaN" % key + assert isinstance(rr.get(key), float) and rr[key] == rr[key], "read %s 缺失/NaN" % key + assert rw["p99_ms"] <= SLA_P99_MS and rr["p99_ms"] <= SLA_P99_MS, ( + "P99 超门禁:write=%.4fms read=%.4fms" % (rw["p99_ms"], rr["p99_ms"])) + # 3) 执行输出中确有 P99 数值行与结论行(真实执行的证据,非源码 grep) + assert "P99=" in out, "探针输出缺失 P99 数值行" + assert "P99 结论:" in out, "探针输出缺失 P99 门禁结论行" + assert "entity_id 一致性校验:PASS" in out, ( + "read 返回行 entity_id 一致性校验未通过(QC #3 防静默放宽门禁)") + # 4) 环境受限的诚实声明(模块元数据校验,附加项) + doc = p99_probe.__doc__ or "" + assert ("不含网络 RTT" in doc) or ("真实库" in doc), "探针缺少环境受限的诚实声明" + assert "--live" in doc, "探针未指明真实库复测路径"