diff --git a/logs/m5a-qc236-verify.md b/logs/m5a-qc236-verify.md new file mode 100644 index 0000000..1140e87 --- /dev/null +++ b/logs/m5a-qc236-verify.md @@ -0,0 +1,96 @@ +# 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 \,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 文件的验收与自证),无需拆分。 diff --git a/logs/m5a-run-meta.txt b/logs/m5a-run-meta.txt index 5ceda7c..32ad66b 100644 --- a/logs/m5a-run-meta.txt +++ b/logs/m5a-run-meta.txt @@ -1,23 +1,25 @@ === M5a 实测运行元信息(logs/m5a-run-meta.txt) === -generated_at : 2026-09-22 19:54:36 +0800 +generated_at : 2026-09-22 19:56:19 +0800 cwd : modules/pbl_evidence interpreter : Python 3.10.12 (/usr/bin/python3) 说明 : 本文件记录本次被运行/被引用的 .py 的路径、字节数、sha256 与 python -m py_compile 真实 rc(rc=0 即语法编译通过)。 - : 本次任务范围仅「运行 + 落日志」,未修改任何源码;未执行任何 git 操作。 + : 本次任务范围仅「运行 + 落日志」,未修改任何源码逻辑,未执行任何 git 操作(add/commit/push 均无)。 + : 环境:无可用 DB(PBL_EVIDENCE_TEST_DB / PBL_M5A_DB 未设置)→ 无 DB,仅跑 fail-closed 路径。 -PATH BYTES RC PY_COMPILE -scripts/selfcheck_m5a.py 43575 0 OK sha256=65d4d0acd0bfba00 -tests/test_m5a_idempotency.py 14706 0 OK sha256=ef214767c6b802dd -tests/m5a_live_db_harness.py 28648 0 OK sha256=01b45a0af96b5f60 +PATH BYTES RC PYC SHA256(16) +scripts/selfcheck_m5a.py 43575 0 OK 65d4d0acd0bfba00 +tests/test_m5a_idempotency.py 14706 0 OK ef214767c6b802dd +tests/m5a_live_db_harness.py 28648 0 OK 01b45a0af96b5f60 ---- 本次产出的日志文件(真实 stdout+stderr 重定向,非占位) --- -logs/m5a-selfcheck-run.log 4682 bytes sha256=75001945d581d5e1 -logs/m5a-selfcheck-run.prev.log 3643 bytes sha256=dabbf7f0152ea435 +--- 本次产出的日志文件(真实 stdout+stderr 重定向,非占位、非引擎 mtime 清单) --- +logs/m5a-selfcheck-run.log 4998 bytes sha256=176a6c635ca03ff1 +logs/m5a-selfcheck-run.prev.log 4682 bytes sha256=75001945d581d5e1 logs/m5a-idempotency-run.log 10277 bytes sha256=b98766f25d914282 +logs/m5a-run-meta.txt self (本文件自身不断言字节数,避免自引用矛盾;现场以 ls -la 为准) --- 运行结果摘要(真实 rc,逐条可回溯到上面日志正文) --- -selfcheck_m5a.py : RC=1 (12 项中 11 PASS / 1 FAIL,FAIL 项为 #11 运行证据日志门禁,详见 m5a-selfcheck-run.log) -pytest test_m5a_idempotency : RC=0 (8 passed, 1 skipped;skip 原因=无 DB,仅跑 fail-closed 路径) -m5a_live_db_harness.py : RC=0 (C-3.1~C-3.4 fail-closed 全 PASS + IDM.1~IDM.10 全 PASS,末行 LIVE DB HARNESS ALL PASS) -m5a-selfcheck-run.prev.log : 覆盖式写入前由 cp 保留的上一次基线输出(对照用,本次未改动) +python3 scripts/selfcheck_m5a.py : RC=1 (12 项 11 PASS / 1 FAIL;FAIL=#11 运行证据日志门禁,判据为 docs/02-develop/m5a-live-db-harness.log 含 traceback,属上游证据日志口径问题,非本次运行引入) +python3 -m pytest tests/test_m5a_idempotency.py -v -rs : RC=0 (8 passed, 1 skipped;skip 原因=无 DB,仅跑 fail-closed 路径) +python3 tests/m5a_live_db_harness.py : RC=0 (C-3.1~C-3.4 fail-closed 全 [PASS] + IDM.1~IDM.10 全 [PASS],末行 LIVE DB HARNESS ALL PASS;注:harness 按设计回落读取 projects/pbls/env/test.json 的 db.sandbox 凭据连上本地沙箱 MariaDB,用后 DROP DATABASE,故非「无 DB」路径,已在 idempotency 日志头部如实更正) +logs/m5a-selfcheck-run.prev.log : 覆盖式写入前 cp 保留的上一次基线输出(对照用) # RC=0