pbl_compiler/docs/work-log-2026-09-19.md

118 lines
8.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.

# pbl_compiler 工作日志 — 2026-09-19(M3b 规则映射与导出 · 整改轮)
任务:`[M3b] pbl_compiler script_engine 规则映射与导出`
(第12章 event→condition→response → script_type=1 规则 JSON 映射、产物导出、game_definition 写入)
角色:agent.develop | 迭代:pbls-初始迭代 | 任务性质:new_dev(含 QC 退回意见逐条整改)
## 1. 本轮范围
只做「登记面接线 + 审计工具自身缺陷 + 提交可追溯性」的增量整改,**不重写已合规的
models/ 表定义本体**(前轮 QC 已确认四段式 / primary str32 / codes parentid 全过)。
| # | 退回意见 | 处置 |
|---|---------|------|
| QC #1(硬门禁·第四章 RBAC) | 5 个 M3b Web 契约未登记 RBAC 路径;init.py 注释条数与代码不符 | 已修:`scripts/load_path.py` 逐条显式登记(无通配符),`pbl_compiler/init.py` 注释与事实对齐 |
| QC #2(流程·真实性) | 交付前未自行 git commit,由引擎代为收口 | 已修:本轮 agent 自行选择性 `git add` + `git commit` + `git push`,commit `cb1132e` |
| 前轮 #2(GD 唯一写入口) | gd_rules_writer 无调用入口 | 保持:`pbl_compiler/api.py` L606 编译主流程 ⑥.5 调用 `write_gd_rules()`,`rules_export_api.py` L223 亦经此入口 |
| 前轮 #3(CRUD editable) | editable 仅 get_data_url,xls2ui 会回退默认写入口 | 保持方案 A:9 个只读拒绝端点 + params 顶层三 URL 绝对路径 |
| 前轮 #4(work log) | 无 dated work log | 本文件补齐归档 |
## 2. 时间线
1. **实跑审计定位根因**:`python3 scripts/audit_rbac_parity.py` → rc=1,报
「load_path.py PATHS 为空(RBAC 零登记 → 全部 403)」。
2. **交叉核验发现是两处事实叠加**:
- 工作区 `scripts/load_path.py` 实际已登记 29 条(`grep -c "('/pbl_compiler/api/"` = 29),
与 `ls wwwroot/api/*.dspy | wc -l` = 29 一致 → QC 引用的「仅 15/20 条」是
**git HEAD 版本**(前轮变更未提交,`git show HEAD:scripts/load_path.py` 实测 20 条、
`git ls-tree HEAD wwwroot/api/` 实测 20 个),审核方读的是仓库而非工作区。
- 审计脚本自身存在解析缺陷:`parse()` 对纯字面量列表已 `literal_eval` 成 python list,
而 `resolved` 字典只收 `ast.List` 形态,导致 `PATHS = PATHS_CONTRACTS + PATHS_READONLY_DENY`
(BinOp)回查 `resolved` 拿到空列表 → 把已登记的 29 条误判成「零登记」,产生假 FAIL。
3. **修审计工具**:`resolved` 同时接受 `list`(已求值)与 `ast.List`(含函数引用求值失败)两种形态。
4. **修注释**:`init.py` 文件头「共 20 条 + 只读列表 CRUD 6 条」→
「共 29 条 = Web 契约端点 20 + CRUD 只读拒绝端点 9,无通配符」。
5. **实跑自证**:audit rc=0 PASS;`scripts/test_m3b_mapping.py` PASS=113 / FAIL=0;
`python3 -m py_compile pbl_compiler/*.py scripts/*.py` OK。
6. **自行收口**:选择性暂存 15 个文件(6 改 + 9 新增 .dspy),`git commit` → `cb1132e`,
`git push origin HEAD` 成功(`d203811..cb1132e HEAD -> main`)。未使用 `git add -A`,
`__pycache__` / 生成物未入库。
## 3. 技术决策与陷阱
- **RBAC 登记分两个集合而非一个**:`PATHS_CONTRACTS`(20,进 init.py CONTRACTS /
__init__.py `__all__` / wwwroot/api)与 `PATHS_READONLY_DENY`(9,**不是** ServerEnv 契约,
只为封死 xls2ui 默认 `add_/update_/delete_<table>.dspy` 写入口)。审计按
`CONTRACTS ∪ READONLY_DENY` 比对,既不漏登记也不产生幽灵登记。
- **只读表为何仍要建 create/update/delete 端点**:crud-definition-spec 要求 editable 三 URL
齐备,缺项时 xls2ui 回退生成默认写入口 → `rules_hash`(29.6 确定性指纹)可被手工改写。
故 9 个端点显式返回 `{"widgettype":"Error","options":{"label":"...只读,禁止手工增删改"}}`,
把「拒绝」变成可机械验证的事实(audit 第 ⑪ 项检查 Error widget)。
- **不登记不存在的路径**:QC #1 提到「若引入 pbl_script_rule 只读列表 CRUD 生成目录则补 6 条」。
实测 `wwwroot/` 下仅有 `api/` 一个子目录(无 CRUD 生成目录),`pbl_script_rule_list` 是手写
Web 契约而非 xls2ui CRUD → 补登 `/pbl_compiler/pbl_script_rule` 等 6 条会形成幽灵登记、
污染 permission 表。此判断已固化进 audit 第 ⑫ 项检查。
- **禁通配符**:29 条全部显式,audit 第 ④ 项用正则扫 `*`/`%` 通配命中即 FAIL。
- **审计工具也要被审计**:`resolved` 只认 AST 形态这一缺陷,会让「已正确整改」被判成
「RBAC 零登记」,白白烧掉退回预算。凡登记面比对,脚本必须同时兼容字面量求值成功/失败两种形态。
## 4. 验证(实跑输出摘录)
```
$ python3 scripts/audit_rbac_parity.py
PASS init.py CONTRACTS = 20 个契约
PASS 20 个契约在 api.py/rules_export_api.py 均有同名实现
PASS __init__.py __all__ 覆盖全部 20 个契约
PASS load_path.py 无通配符(29 条全显式)
PASS load_path.py 登记 29 条无重复(契约 20 + 只读拒绝 9)
PASS PATHS_CONTRACTS 与 CONTRACTS 一一对应(20 条)
PASS PATHS 登记 29 条,与期望端点集合(契约 20 + 只读拒绝 9)一一对应
PASS wwwroot/api/*.dspy = 29 个,与 CONTRACTS∪READONLY_DENY 一一对应
PASS grep 口径自证:PATHS 中 /api/ 条目=29,wwwroot/api/*.dspy=29
PASS 29 个 .dspy 无非法 import(仅允许 sqlor.filter)
PASS 29 个 .dspy 均有显式 return
PASS .dspy 无 ServerEnv() 取请求态
PASS 9 个只读拒绝端点均显式返回 Error widget(CRUD 写入口已封死)
PASS wwwroot/ 无 CRUD 生成子目录(只读列表走手写契约 api/pbl_script_rule_list.dspy)
PASS json/*.json 共 3 个:根键白名单(['_comment','alias','params','tblname','title']) / alias
/ editable 三 URL 齐备且指向真实 .dspy / 字段与 models 全对齐
RESULT: PASS (contracts=20, readonly_deny=9, dspy=29, rbac_api_paths=29, json=3) rc=0
$ python3 scripts/test_m3b_mapping.py
PASS: 113 FAIL: 0
$ grep -c "('/pbl_compiler/api/" scripts/load_path.py → 29
$ ls wwwroot/api/*.dspy | wc -l → 29
$ grep -n 'pbl_compiler_export_rules|pbl_compiler_rules_preview|pbl_compiler_rules_diff|
pbl_game_definition_rules_get|pbl_script_rule_list' scripts/load_path.py
→ 68,69,70,71,72 五行命中(5 个 M3b 契约全部显式登记)
$ git log --oneline -1 → cb1132e fix(pbl_compiler): M3b RBAC 29条显式登记+...
$ git push origin HEAD → d203811..cb1132e HEAD -> main
```
## 5. 环境受限项(如实说明,不谎报已验证)
1. **`~/repos/sage` 不存在**:QC 改法要求
`cd ~/repos/sage && ./py3/bin/python ~/repos/pbl_modules/pbl_compiler/scripts/load_path.py`
并「同步中央 sage/load_path.py 兜底」。本工作空间实测 `ls -d ~/repos/sage` → 无该目录,
中央 `sage` 仓库与 `py3` 虚拟环境均不在位;`apps/pbls/` 下也**没有**中央 load_path.py
(全库 `grep -rln "pbl_compiler/api/" apps/` 零命中),各 pbl_* 模块一律以
`modules/{模块}/scripts/load_path.py` 为唯一登记面。故本轮只完成模块侧登记与自证,
中央兜底无法执行,需部署角色在有 sage 的环境跑 `register()`。
2. **未连库、未起服务**:`register()` 依赖 rbac CLI(`set_role_perm.py`)与 MariaDB,
本工作空间无 DB 实例(`PBLS_DB_USER` 空、mysql 未授权)。脚本设计为 CLI 不在位时
打印 PENDING 清单并以 rc=1 暴露(不静默跳过),部署阶段据此补登。
3. **未重跑 xls2ui/build.sh**:`wwwroot/` 无 CRUD 生成子目录(三表按只读列表呈现,
走手写契约),本轮无 .ui 产物需再生成;若后续引入 CRUD 目录,audit 第 ⑫ 项会 FAIL 提醒补登。
4. **「登录后调用不再 403」未做端到端实测**:受 1/2 限制,只能以「登记面与端点集合一一对应」
作为可达性的机械证明,真实 HTTP 200 验证需部署环境。
## 6. 分支状态
- 仓库:`modules/pbl_compiler`(origin `git@git.opencomputing.cn:yumoqing/pbl_compiler.git`)
- 分支:`main`,HEAD = `cb1132e`(agent.develop 自行提交并推送,非引擎代为收口)
- 工作区:暂存区已清空;未跟踪仅剩 `docs/work-log-2026-09-19.md`(本文件,随交付入库)
与 `__pycache__`(.gitignore 已排除)
- 前序 6 个 `deliver: 交付收口(引擎代为提交)` 提交(626b2c0/eb18a7d/4051e9d/c276af5/2c14bf5/d203811)
为历史事实,本轮不改写历史,仅在其上追加 agent 自有提交。