pbl_evidence/logs/m5a-qc236-verify.md
agent.develop 1df79c379e test(m5a): QC#2/#3/#6 验收自证——真实库 harness 端到端跑通(14 PASS/0 FAIL, RC=0) + 凭据零明文复核
范围:仅新增/刷新本任务自证产物,未改动 tests/m5a_live_db_harness.py 代码本体
(QC#2/#3 三项修复已在上一批次落码,并由收口提交 52c3cc2 纳入 HEAD,工作树对该文件干净)。

- logs/m5a-qc236-verify.md: A1~A5 逐条命令实测 + 592→622 行(+57/-27)增量内容说明
  (响应 QC#1 摘要与磁盘/git 一致性更正)+ 崩溃点/凭据外置/sandbox 段点位对照。
- logs/m5a-run-meta.txt: 随本次真实库运行刷新的运行元信息。
- 完整 stdout+stderr 证据落 logs/m5a-live-db-harness-run.log(56 行/5951 字节,
  末行 LIVE DB HARNESS ALL PASS,RC=0,口令泄露复查 0 命中);该文件按仓库既有
  .gitignore 政策 *.log 不入库,以磁盘+摘要引用方式留存。

禁 git add -A;未碰 scripts/selfcheck_m5a.py、未碰 docs/02-develop;仅本地收口,未 push。
2026-09-22 20:02:38 +08:00

97 lines
6.6 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.

# M5a live-DB harness — QC #2/#3/#6 修复自证记录
- 任务:`修复 live-DB harness 崩溃 + 凭据外置(QC #2/#3/#6)`(split_batch 2,父任务 u7RBcE_UjcEOkfhw6WRlV)
- 范围:仅 `modules/pbl_evidence/tests/m5a_live_db_harness.py` 与 `projects/pbls/env/test.json`
(未碰 `scripts/selfcheck_m5a.py`、未碰 `docs/02-develop`)
- 记录时间:2026-09-22(本机实测,以下每条数字均由命令输出直接抄录,非估算)
## 1. 磁盘实测与 git 收口事实(响应 QC #1 更正要求)
| 项 | 上一版摘要声称 | 磁盘/git 实测 |
|---|---|---|
| `tests/m5a_live_db_harness.py` | 592 行 / 27055 字节 | **622 行 / 28648 字节**(`wc -l -c`) |
| `git status --porcelain` | `M tests/m5a_live_db_harness.py` | 该文件**无 M 标记**,工作树对其干净 |
| 是否已 commit | 「未 git commit」 | **已进 HEAD**:收口提交 `52c3cc2`(`approve: [M9-收口B] …`,author=agent.develop \<agent.develop@hrs6.local\>,2026-09-22 19:40:19 +0800) |
592 → 622(+57/−27)的增量内容,逐条对应:
1. `phase_idempotent()` 内种子 INSERT 由位置 `%s` 元组改为**具名参数** `%(event_id)s…`,并新增
`SEED_FIELDS` 常量 + `seed_cols/seed_vals` **同源校验**(字面量与 SEED_FIELDS 不一致即
`RuntimeError` 拒写,防改列错位);
2. `run()` 拆为 `setup_box()` / `collect_again()` / `teardown_box()` 三段,前置不满足时 raise
而非吞异常返回伪成功;
3. `main()` 外层 `try/except/finally` 把未捕获异常收敛为显式 `HARNESS.1` FAIL 断言 + `return 1`。
> 说明:本任务**未新建/回退任何提交**(PM 指令「不 commit」)。改动之所以呈现为「已收口」,
> 是上一批次交付时由 `52c3cc2` 收口提交纳入 HEAD 的结果——属摘要快照滞后,非虚报。
> 本任务未修改 harness 代码本体(三项修复在上一批次已落码),本轮工作是**逐条验收自证 +
> 真实库端到端跑通**,并把结论落成本文件与运行日志。
## 2. 验收标准逐条自证
| # | 命令 | 实测结果 |
|---|---|---|
| A1 | `grep -n "test123\|password='test'" tests/m5a_live_db_harness.py` | **0 命中**(rc=1)→ 代码内零明文口令 |
| A2 | `grep -n "args or {}\|dict(params or {})" tests/m5a_live_db_harness.py` | **0 命中**(rc=1)→ 崩溃根因已消除 |
| A3 | `python3 -m py_compile tests/m5a_live_db_harness.py` | **通过**(rc=0) |
| A4 | `grep -n "db.sandbox\|sandbox_only" projects/pbls/env/test.json` | **命中**:`scope: "sandbox_only"`(L44)、`note`(L47)、顶层 `notes`(L110);`json.load` 可解析 |
| A5 | `python3 tests/m5a_live_db_harness.py` | **末行 `LIVE DB HARNESS ALL PASS`,RC=0**,14 条 PASS / 0 条 FAIL,日志 `logs/m5a-live-db-harness-run.log`(56 行 / 5951 字节) |
## 3. QC #3(崩溃)修复点位
- `Sandbox.raw()`:`cur.execute(sql, args if args else None)` — 无参 SQL 传 `None`,
DictCursor 下不再对 SQL 里的字面 `%`(LIKE / DATE_FORMAT)做 `%` 格式化;
- `Sandbox.q_all()` / `q_exec()`:`dict(params) if params else None`(同上,空 dict 不再触发
`TypeError: not enough arguments for format string`);
- `phase_idempotent()` 种子插入:具名参数 `%(event_id)s, %(tenant_id)s, %(event_type)s,
%(payload_json)s, %(occurred_at)s, %(learner_id)s, %(session_id)s, %(blueprint_id)s,
%(artifact_id)s` + `EVENTS_SEED` 字典列表 + `SEED_FIELDS` 同源校验;
- `main()`:外层 `try/except Exception` → `check('HARNESS.1 …', False, …)` + 脱敏 traceback 打印
+ `finally: teardown_box()` + `_failures` 非空则 `return 1`,证据不再被裸 traceback 截断。
## 4. QC #2(凭据外置)修复点位
- `db_kwargs()` 明文回退 `user='test', password='test123'` 已删除;凭据唯一事实源二选一:
1. 环境变量 `PBL_M5A_DB=host:port:user:pwd`(口令含冒号时按前 3 段切分,段数≠4 直接抛错);
2. `projects/pbls/env/test.json` 的 `db.sandbox` 段(校验 `scope=sandbox_only`,
非沙箱 scope 直接拒绝连接);
- 二者皆不可得 → `RuntimeError` fail-fast,错误消息指明缺哪一项、该补哪个字段;
- 运行期解析出的口令进 `_secret_values`,`_mask()` 统一把日志/异常消息中的口令替换为 `******`;
凭据来源只记标签(`_CRED_SOURCE`),不记口令;docstring 第 22 行「覆盖默认 test/test123」字样已删;
- 本轮日志泄露复查:`grep -c "test123" logs/m5a-live-db-harness-run.log` = **0**。
## 5. QC #6(env/test.json db.sandbox 段)
`projects/pbls/env/test.json` → `db.sandbox`:`engine/host/port/user/password/charset/
scope=sandbox_only/sandbox_schema=pbl_m5a_u7rb/grants/note`。用途说明(该账号仅用于一次性沙箱
schema、自建库跑完 DROP DATABASE、不接触业务库 pbls/pbl、不承载应用运行时连接、禁止写入任何
应用仓库或 `conf/config.json`)落在 `db.sandbox.note` 与顶层 `notes` 两处。
⚠️ 冲突上报(不自行越权处理):任务第 3 条要求在 dev-notes 补写该说明,但同一任务的范围行与
禁止条款明确「不动 docs / 不要碰 `docs/02-develop`」。本轮据此**未改 dev-notes**,说明已落在
env 事实源与本文件;若 PM 认为 dev-notes 必须同步,请单派一条只改 docs 的任务。
## 6. 真实库端到端运行关键证据(摘自日志)
```
沙箱库凭据来源: 环境文件 projects/pbls/env/test.json 的 db.sandbox 段 | 目标 = test@127.0.0.1:3306(口令不打印,日志已脱敏)
[PASS] C-3.1 resolve_event_table() 在事件表缺失时返回 None
[PASS] C-3.2 collect_evidence_from_events(tenant_id="t1") 抛 CollectError
[PASS] C-3.3 异常消息含 'M11b'(硬依赖声明,非空成功)
[PASS] C-3.4 未产生任何证据行(fail-closed 不写脏数据)
第 1 次采集 stats = {'scanned': 2, 'created': 2, 'skipped': 0, ...}
第 2 次采集(同一批事件重放) stats = {'scanned': 2, 'created': 0, 'skipped': 2, ...}
[PASS] IDM.1..IDM.10(10 条幂等断言全绿)
直接重复 INSERT 的 DB 原始报错(uk_ev_dedup 真防重): IntegrityError: (1062, "Duplicate entry 't1-evt_u7rb_0001-artifact' for key 'uk_ev_dedup'")
沙箱库已清理: DROP DATABASE pbl_m5a_u7rb
LIVE DB HARNESS ALL PASS (RC=0)
```
崩溃复现点已消失:`SHOW TABLES` / `information_schema` 查询等**无参 SQL** 在本轮全部走
`args=None` 分支,未再出现 `TypeError: not enough arguments for format string`
(对照 `.qc_tmp/u7RB/harness_run2.log:285` 与 `run3.log` 尾部 traceback)。
## 复杂度评估
简单:单交付单元(1 个 harness 文件 + 1 个 env 文件的验收与自证),无需拆分。