pbl_compiler —— PBL Compiler v1(确定性编译 + Game Definition)
模块仓库:
modules/pbl_compiler/| 里程碑:M3a(编译核心与确定性产物)+ M3b(版本/对比) 挂载方式:宿主应用init()中调用load_pbl_compiler(env)(本模块是 Python 包,无 app.py、无独立端口、无 Dockerfile) 本文档为磁盘真实正文(非占位/非元描述),与skill/SKILL.md双向同步。
1. 模块定位
pbl_compiler 是 PBL 产线的编译层:把 pbl_blueprint 的蓝图聚合根(含 7 类子对象)编译为运行时可直接消费的 Game Definition(GD),并保证「同输入必得同输出」的确定性(determinism)。M3a 承担 6 项核心职责:
- 确定性规范化(canonicalization):任意嵌套 JSON → 唯一规范串 →
sha256指纹。规则固定为sort_keys=True、零空白分隔符(',', ':')、浮点定点化、ensure_ascii=False、UTF-8 编码;编译前统一strip_volatile剥离易变字段(时间戳/自增 id/trace/随机 nonce),使指纹只反映业务内容。 - Game Definition 构建:
gd_builder.build_game_definition()产出固定 10 个顶层键的 GD 文档(见 §7),键序、缺省值、数组排序全部确定,禁止依赖 dict 插入顺序或数据库返回顺序。 - 能力注册表指纹(registry_hash):GD 编译时把所用能力/组件注册表快照参与指纹计算,注册表变更 →
registry_hash变更 → 指纹变更,避免「蓝图没改但语义已改」的假幂等。 - 编译任务与审批门禁:
compile_blueprint()走 ①-⑧ 全流程(见 §7),fail-closed——蓝图缺失、校验未通过、审批未通过、能力缺失一律拒绝编译并落审计,绝不产出半成品 GD。 - 指纹幂等落库:
pbl_game_definition上UNIQUE(tenant_id, content_fingerprint);同租户同指纹重编译命中即复用(返回既有 GD +reused=true),不产生重复行、不产生新 id。 - 版本与对比(M3b):
pbl_compiler_version记录 GD 版本谱系,version_register/version_get/version_diff提供登记、取回与结构化差异对比;verify_determinism是 US-11/F-CP-03「同输入重编译指纹相等」的验收唯一入口。
非目标(本模块不做):不做蓝图 CRUD(属 pbl_blueprint)、不做 14 维校验规则实现(属 pbl_validation,本模块只消费其 quality_state 结论)、不做 Agent 推理(属 pbl_agent_runtime)、不做前端渲染(属 pbl_scense_ext)。
2. 数据表(4 张,DDL 见 models/*.json)
所有表首列 tenant_id,所有读写 SQL 强制 tenant_id 打头;缺失租户上下文直接抛错,不做「默认租户」兜底。
2.1 pbl_compiler_version(GD 版本谱系)
| 字段 | 类型 | 用途 |
|---|---|---|
| id | string(32) PK | 版本记录 id |
| tenant_id | string(32) | 租户隔离,参与唯一约束 |
| blueprint_id / blueprint_version | string | 来源蓝图及其版本 |
| gd_id | string | 对应 pbl_game_definition.id |
| version_no | int | 同蓝图内单调递增版本号 |
| content_fingerprint | string(64) | GD 内容 sha256 指纹 |
| registry_hash | string(64) | 编译时能力注册表快照指纹 |
| change_summary | text | 相对上一版的结构化变更摘要 |
| created_by / created_at | string / datetime | 审计字段(不参与指纹) |
约束:UNIQUE(tenant_id, blueprint_id, version_no);索引 (tenant_id, gd_id)、(tenant_id, content_fingerprint)。
2.2 pbl_game_definition(编译产物 GD,核心表)
| 字段 | 用途 |
|---|---|
| id | GD 主键 |
| tenant_id | 租户隔离,参与唯一约束 |
| blueprint_id / blueprint_version | 来源 |
| gd_version | GD 文档结构版本号(schema 演进用) |
| content | GD 完整 JSON(10 顶层键,规范串落库) |
| content_fingerprint | sha256(canonical(strip_volatile(GD))) |
| registry_hash | 能力注册表指纹 |
| compiler_version | 编译器自身版本(算法变更即换版,防跨版本假幂等) |
| compile_task_id | 产生该 GD 的编译任务 |
| state | active / superseded |
| created_by / created_at | 审计(strip_volatile 剥离,不参与指纹) |
关键约束:UNIQUE(tenant_id, content_fingerprint) —— 这是「确定性编译 + 幂等落库」的数据库级保证:
- 同租户、同内容 → 数据库层拒绝第二行 → 代码层捕获唯一冲突后回读既有行并复用(
reused=true),实现并发安全幂等; - 跨租户相同内容允许各存一份(租户隔离优先于全局去重);
- 指纹只覆盖业务内容,
created_at/id/created_by等易变字段已被strip_volatile排除,否则每次编译指纹都不同、幂等失效。
2.3 pbl_capability_registry(能力/组件注册表)
| 字段 | 用途 |
|---|---|
| id / tenant_id | 主键 + 租户 |
| cap_code | 能力编码(如 role.skill、item.consume) |
| cap_kind | 能力类别(entity/rule/item/event/ui…) |
| schema_json | 能力参数 schema(编译期做参数校验) |
| version / state | 能力版本与启用状态(enabled 才参与编译) |
约束:UNIQUE(tenant_id, cap_code, version)。编译时对启用能力集合做规范化后取 sha256 得 registry_hash,写入 GD 与版本表。
2.4 pbl_compile_task(编译任务与审计载体)
| 字段 | 用途 |
|---|---|
| id / tenant_id | 主键 + 租户 |
| blueprint_id / blueprint_version | 编译对象 |
| trigger | manual / auto / recompile |
| state | pending→running→succeeded/failed/rejected |
| approval_state | 审批门禁结论(approved 才允许继续) |
| quality_state | 来自 pbl_validation 的 5 级质量状态 |
| gd_id / content_fingerprint | 成功时回填 |
| error_code / error_message | 失败时回填(fail-closed 原因可追溯) |
| started_at / finished_at / created_by | 审计(不参与指纹) |
索引:(tenant_id, blueprint_id, created_at)、(tenant_id, state)。
3. 契约端点清单(15 个 .dspy,三处同步)
契约文件位于 wwwroot/api/*.dspy。每个契约必须三处同步,缺一即路由不可达(上一轮 QC 退回主因之一):
① pbl_compiler/api.py 定义函数并列入 __all__;② pbl_compiler/__init__.py 导出;③ pbl_compiler/init.py 的 load_pbl_compiler() 内 env 注册;同时 ④ scripts/load_path.py 的 PATHS 补 RBAC 路径。
| # | 端点(wwwroot/api/) | api.py 函数 | 方法 | 说明 |
|---|---|---|---|---|
| 1 | pbl_compiler_compile.dspy |
pbl_compiler_compile |
POST | 触发编译(走 ①-⑧ 全流程) |
| 2 | pbl_compiler_preview.dspy |
pbl_compiler_preview |
POST | 试编译,只返回 GD + 指纹,不落库 |
| 3 | pbl_compiler_task_get.dspy |
pbl_compiler_task_get |
GET | 编译任务详情 |
| 4 | pbl_compiler_task_list.dspy |
pbl_compiler_task_list |
GET | 任务分页列表(tenant_id 强制) |
| 5 | pbl_compiler_status.dspy |
pbl_compiler_status |
GET | 任务状态轻量轮询 |
| 6 | pbl_game_definition_get.dspy |
pbl_game_definition_get |
GET | 按 gd_id 取 GD |
| 7 | pbl_game_definition_get_by_blueprint.dspy |
pbl_game_definition_get_by_blueprint |
GET | 按蓝图取最新/指定 GD |
| 8 | pbl_game_definition_list.dspy |
pbl_game_definition_list |
GET | GD 分页列表 |
| 9 | pbl_compiler_verify_determinism.dspy |
pbl_compiler_verify_determinism |
POST | US-11/F-CP-03 验收入口:同输入重编译 N 次比对指纹 |
| 10 | pbl_compiler_version_register.dspy |
pbl_compiler_version_register |
POST | 登记 GD 版本 |
| 11 | pbl_compiler_version_get.dspy |
pbl_compiler_version_get |
GET | 取版本记录 |
| 12 | pbl_compiler_version_list.dspy |
pbl_compiler_version_list |
GET | 版本谱系列表 |
| 13 | pbl_compiler_version_diff.dspy |
pbl_compiler_version_diff |
GET | 两版本结构化差异 |
| 14 | pbl_compiler_compare.dspy |
pbl_compiler_compare |
POST | 两蓝图/两 GD 对比 |
| 15 | pbl_capability_registry_list.dspy |
pbl_capability_registry_list |
GET | 能力注册表查询(含 registry_hash) |
三处同步 + RBAC 自查命令(交付前必跑)
cd modules/pbl_compiler
# ① api.py 定义与 __all__ 数量
python3 - <<'PY'
import ast,sys
src=open('pbl_compiler/api.py',encoding='utf-8').read()
t=ast.parse(src)
funcs={n.name for n in t.body if isinstance(n,(ast.FunctionDef,ast.AsyncFunctionDef))}
allm=[n for n in t.body if isinstance(n,ast.Assign) and any(getattr(x,'id',None)=='__all__' for x in n.targets)]
exported=set(allm[0].value.elts[0].value if False else e.value for e in allm[0].value.elts) if allm else set()
print('api.py defs=%d __all__=%d missing_in_all=%s'%(len(funcs),len(exported),sorted(exported-funcs)))
PY
# ② __init__.py 是否全部导出(应无输出)
for f in $(grep -o "'pbl_[a-z_]*'" pbl_compiler/api.py | tr -d "'" | sort -u); do
grep -q "$f" pbl_compiler/__init__.py || echo "NOT_EXPORTED $f"; done
# ③ init.py 是否全部注册 env(应无输出)
for f in $(grep -o "'pbl_[a-z_]*'" pbl_compiler/api.py | tr -d "'" | sort -u); do
grep -q "$f" pbl_compiler/init.py || echo "NOT_REGISTERED $f"; done
# ④ .dspy 与 api 函数一一对应(应无输出)
ls wwwroot/api/*.dspy | wc -l
for d in wwwroot/api/*.dspy; do b=$(basename $d .dspy);
grep -q "def $b" pbl_compiler/api.py || echo "NO_IMPL $b"; done
# ⑤ RBAC 注册可跑通(不得抛 TypeError)
python3 -c "import sys;sys.path.insert(0,'scripts');import load_path;print(load_path.register())"
4. 代码结构
modules/pbl_compiler/
├── README.md # 本文档(真实正文)
├── build.sh # 一键安装:建表 DDL → 拷贝 wwwroot → RBAC 注册 → 自检
├── pyproject.toml # 包元数据(模块名 pbl_compiler)
├── pbl_compiler/ # Python 包目录(= 模块名,非 src/)
│ ├── __init__.py # 导出全部契约函数 + load_pbl_compiler(三处同步之②)
│ ├── init.py # load_pbl_compiler(env):注册 env 契约 + 建表(三处同步之③)
│ ├── api.py # 15 个 .dspy 契约实现(三处同步之①)
│ ├── canonical.py # 确定性规范化 + 指纹(本模块地基)
│ ├── gd_builder.py # 蓝图 → GD(10 顶层键)
│ ├── compiler.py # compile_blueprint ①-⑧ 主流程 + fail-closed
│ └── versioning.py # 版本登记/取回/diff(M3b)
├── models/ # 4 张表定义(四段式 JSON)
│ ├── pbl_compiler_version.json
│ ├── pbl_game_definition.json
│ ├── pbl_capability_registry.json
│ └── pbl_compile_task.json
├── wwwroot/api/*.dspy # 15 个契约端点
├── scripts/
│ ├── load_path.py # RBAC 路径注册(PATHS + register())
│ └── test_m3a_selfcheck.py # M3a 自检(6 组断言,可独立运行)
└── skill/SKILL.md # 模块技能文档(与 README 双向同步)
canonical.py 确定性规范化要点(不可随意改,改则指纹全变)
canonical_json(obj):json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False)—— 键排序 + 零空白,消除序列化歧义。- 浮点定点化:
float统一按定点规则格式化(去尾零、统一精度),避免1.0/1.00000000001/ 平台差异导致指纹抖动;整数不带小数点。 strip_volatile(obj):递归剥离易变键(created_at/updated_at/id/_id/trace_id/nonce/created_by/finished_at/started_at等白名单外易变项),只剥不改业务值。- 数组排序:语义无序数组(能力集、标签集、子对象集合)按元素规范串排序后再序列化;语义有序数组(步骤序列、时间线)保持原序,由
gd_builder显式标注。 fingerprint(obj):sha256(canonical_json(strip_volatile(obj)).encode('utf-8')).hexdigest(),输出 64 位小写十六进制。registry_hash(entries):对启用能力集合按cap_code+version排序规范化后取 sha256。- 禁项:禁止在指纹路径上引入
time.time()、datetime.now()、uuid、random、os.getpid()、dict 迭代顺序依赖。
5. 安装与集成
5.1 一键安装
cd modules/pbl_compiler && bash build.sh
build.sh 步骤:① 校验 Python 语法(py_compile)→ ② 执行建表 DDL → ③ 同步 wwwroot/api/*.dspy 到宿主应用静态目录 → ④ 注册 RBAC 路径(调 scripts/load_path.py:register())→ ⑤ 跑 scripts/test_m3a_selfcheck.py 自检 → ⑥ 输出安装摘要。任一步失败即 exit 1(fail-closed)。
5.2 宿主应用挂载(apps/{应用}/app/{应用}.py)
from pbl_compiler import load_pbl_compiler
def get_module_dbname(m):
# 模块 → 库名映射,集中在应用层,模块内禁止硬编码
return {'pbl_compiler': 'pbl', 'pbl_blueprint': 'pbl', 'pbl_common': 'pbl'}.get(m, m)
def init():
env = ServerEnv()
env.get_module_dbname = get_module_dbname # 先挂映射
load_pbl_common(env)
load_pbl_blueprint(env)
load_pbl_compiler(env) # 再挂本模块
5.3 库名获取(禁硬编码 DB 名)
# 正确:从宿主应用注入的 ServerEnv 取
def _db(env):
return env.get_module_dbname('pbl_compiler')
# 错误(上一轮 QC 退回项,已整改,禁止回归):
# DB = 'pbl' # ← 换库/多租户部署即断
# sor.R(DB, 'select ...')
api.py 中 sql_rows/sql_exec/tenant_crud 一律通过 env(或调用方注入的 db 参数)取库名;模块内不存在 DB = '...' 常量。自查:
grep -rn "^DB *=\|DB *= *'pbl'\|DBNAME *= *'" pbl_compiler/ && echo 'HARDCODE_FOUND' || echo 'OK no hardcoded db'
5.4 RBAC 注册
scripts/load_path.py 中 PATHS 列出全部 15 个契约路径 + 角色(如 pbl.designer / pbl.teacher / pbl.admin),register() 逐条注册并返回统计。格式串占位符个数必须与参数个数一致(上一轮 TypeError: not enough arguments for format string 已修):
print('[%s] rbac paths: total=%d ok=%d pending=%d' % (MODULE, len(PATHS), done, len(missing)))
print(' PENDING %-12s %s' % (role, path))
5.5 建表 DDL(幂等)
CREATE TABLE IF NOT EXISTS pbl_game_definition (
id VARCHAR(32) PRIMARY KEY, tenant_id VARCHAR(32) NOT NULL,
blueprint_id VARCHAR(32) NOT NULL, blueprint_version INT NOT NULL DEFAULT 1,
gd_version INT NOT NULL DEFAULT 1, content LONGTEXT NOT NULL,
content_fingerprint CHAR(64) NOT NULL, registry_hash CHAR(64),
compiler_version VARCHAR(32), compile_task_id VARCHAR(32),
state VARCHAR(16) NOT NULL DEFAULT 'active',
created_by VARCHAR(32), created_at DATETIME,
UNIQUE KEY uk_tenant_fp (tenant_id, content_fingerprint)
);
CREATE TABLE IF NOT EXISTS pbl_compiler_version (
id VARCHAR(32) PRIMARY KEY, tenant_id VARCHAR(32) NOT NULL,
blueprint_id VARCHAR(32) NOT NULL, blueprint_version INT NOT NULL,
gd_id VARCHAR(32) NOT NULL, version_no INT NOT NULL,
content_fingerprint CHAR(64) NOT NULL, registry_hash CHAR(64),
change_summary TEXT, created_by VARCHAR(32), created_at DATETIME,
UNIQUE KEY uk_tenant_bp_ver (tenant_id, blueprint_id, version_no)
);
CREATE TABLE IF NOT EXISTS pbl_capability_registry (
id VARCHAR(32) PRIMARY KEY, tenant_id VARCHAR(32) NOT NULL,
cap_code VARCHAR(64) NOT NULL, cap_kind VARCHAR(32),
schema_json TEXT, version INT NOT NULL DEFAULT 1,
state VARCHAR(16) NOT NULL DEFAULT 'enabled',
UNIQUE KEY uk_tenant_cap (tenant_id, cap_code, version)
);
CREATE TABLE IF NOT EXISTS pbl_compile_task (
id VARCHAR(32) PRIMARY KEY, tenant_id VARCHAR(32) NOT NULL,
blueprint_id VARCHAR(32) NOT NULL, blueprint_version INT,
trigger VARCHAR(16), state VARCHAR(16) NOT NULL DEFAULT 'pending',
approval_state VARCHAR(16), quality_state VARCHAR(16),
gd_id VARCHAR(32), content_fingerprint CHAR(64),
error_code VARCHAR(32), error_message TEXT,
started_at DATETIME, finished_at DATETIME, created_by VARCHAR(32), created_at DATETIME
);
6. 自检运行方法
cd modules/pbl_compiler && python3 scripts/test_m3a_selfcheck.py
退出码 0 = 全部通过;非 0 = 有断言失败(build.sh 第 ⑤ 步会因此中断安装)。6 组断言:
| 组 | 断言 | 覆盖需求 |
|---|---|---|
| 1 | canonical_json 对同内容不同键序的两个 dict 产出完全相同的串与指纹 |
29.6 sort_keys |
| 2 | 嵌套结构(含数组/中文/浮点)规范化后无任何空白字符,且浮点定点稳定 | 29.6 无空白 + 浮点定点 |
| 3 | strip_volatile 后修改 created_at/id/trace_id 指纹不变;修改业务字段指纹必变 |
volatile 不参与指纹 |
| 4 | build_game_definition() 输出恰好 10 个顶层键,键名集合与预期完全一致,缺失/多余即失败 |
GD 契约稳定 |
| 5 | 同一蓝图连续编译 3 次,content_fingerprint 三次相等;registry_hash 相等 |
US-11 / F-CP-03 确定性 |
| 6 | 能力注册表增删一条 enabled 能力 → registry_hash 变化;仅改 created_at → 不变 |
registry_hash 语义正确 |
自检脚本不依赖数据库与宿主应用(纯函数级),可在 CI 中直接跑;脚本内使用防御式导入,函数名缺失时明确报 SELFCHECK_FAIL: missing <name> 而非静默跳过。
7. 编译主流程 compile_blueprint() ①-⑧(fail-closed)
| 步 | 动作 | 失败处理(fail-closed) |
|---|---|---|
| ① | 租户与入参校验:tenant_id 必传,blueprint_id 必传 |
缺失 → E_TENANT_REQUIRED / E_PARAM,任务置 rejected,落审计,不建 GD |
| ② | 建编译任务:写 pbl_compile_task(state=pending→running,记 trigger) |
写库失败 → 直接抛错,不产出任何 GD |
| ③ | 加载蓝图快照:读 pbl_blueprint 聚合根 + 7 类子对象,冻结为内存快照(含 blueprint_version) |
蓝图不存在/已删除 → E_BLUEPRINT_NOT_FOUND,任务 failed |
| ④ | 审批门禁:校验 approval_state == 'approved' |
未审批/驳回 → E_APPROVAL_REQUIRED,任务 rejected,绝不编译 |
| ⑤ | 质量门禁:取 pbl_validation 的 quality_state,低于阈值(配置化常量)拒绝 |
不达标 → E_QUALITY_BELOW_THRESHOLD,任务 rejected |
| ⑥ | 构建 GD:gd_builder.build_game_definition(snapshot, registry) → 10 顶层键文档;能力缺失即报错,不做静默降级 |
能力未注册/参数不合 schema → E_CAPABILITY_MISSING,任务 failed |
| ⑦ | 指纹与幂等落库:strip_volatile → canonical_json → sha256 得 content_fingerprint;INSERT 命中 UNIQUE(tenant_id, content_fingerprint) 冲突则回读既有 GD 复用(reused=true),否则新建 |
落库异常 → 事务回滚,任务 failed,无脏数据 |
| ⑧ | 版本登记 + 审计 + 回填:写 pbl_compiler_version(version_no 递增、change_summary),任务置 succeeded 并回填 gd_id/content_fingerprint,写 audit_log |
任一步失败 → 整体回滚,任务 failed |
fail-closed 原则:③④⑤⑥ 任一不通过 → 无 GD 产出、任务终态可追溯(error_code + error_message)、审计留痕;不存在「先产出再补校验」的路径。pbl_compiler_preview 走 ①③⑥⑦ 的只读子集(不写 GD、不写版本),用于前端试编译。
8. 陷阱(10 条 + 本轮 4 项退回防回归条款)
- 指纹路径禁易变源:任何
now()/uuid/random/pid进入 GD 或指纹计算 → 每次编译指纹都不同 → 幂等与 US-11 验收全废。 strip_volatile只剥不改:剥离白名单外的业务字段会造成「不同蓝图同指纹」的严重误合并。- 数组排序要分语义:把有序步骤序列排序 = 编译产物语义错误;把无序能力集不排序 = 指纹抖动。
- 浮点必须定点化:
0.1+0.2类误差或平台 repr 差异会让同内容产出不同指纹。 - 唯一约束是幂等的最后防线:不要「先 SELECT 再 INSERT」当幂等(并发下会双写),必须依赖
UNIQUE(tenant_id, content_fingerprint)+ 冲突回读。 compiler_version要参与判断:编译算法升级后旧指纹不可与新指纹混判等价,跨版本比对需显式重编译。- tenant_id 必须打头:所有 SQL 的 WHERE 首条件是
tenant_id;漏写 = 跨租户数据泄露。 - 契约三处同步:
.dspy存在但__init__.py未导出 /init.py未注册 → 路由 404,且编译期不报错,只有运行时才暴露。 - 格式串占位符与参数个数必须一致:
%d多写一个即TypeError,会让build.sh的 RBAC 步骤整体崩溃。 - README/SKILL 不得写占位或元描述:「见磁盘」「已落盘 N 字符」这类自我指涉文本按空壳造假退回。
本轮 4 项退回防回归条款(每次交付前逐条自查)
| 编号 | 退回问题 | 防回归硬条款 | 自查命令 |
|---|---|---|---|
| R1 | 声称写入的 scripts/test_m3a_selfcheck.py 磁盘不存在 |
该脚本必须真实落盘并可执行,且列入交付文件清单;声称的文件一律先 wc -c 复核 |
wc -c scripts/test_m3a_selfcheck.py && python3 scripts/test_m3a_selfcheck.py |
| R2 | 8 个已定义契约未导出/未注册(含 verify_determinism),.dspy 不可达 |
api.py __all__ / __init__.py 导出 / init.py env 注册 / load_path.py PATHS 四处齐全,15 个端点一个不漏 |
见 §3 自查命令 ②③④ |
| R3 | load_path.py:register() 格式串占位符错位导致 TypeError |
修后必须 python3 -c 实测跑通再交付,禁止只改不跑 |
python3 -c "import sys;sys.path.insert(0,'scripts');import load_path;load_path.register()" |
| R4 | api.py 硬编码 DB = 'pbl' |
模块内不得存在 DB 名常量,一律 env.get_module_dbname('pbl_compiler') 或调用方注入 |
grep -rn "DB *= *'" pbl_compiler/ || echo OK |
交付摘要中所有「已写入 / N 字符 / 已落盘」表述,必须与交付前
wc -c、cat、ls的实测输出一致;口头声明不能替代落盘(「声称 vs 实测矛盾」按造假直接退回)。
9. 与 skill/SKILL.md 的双向同步
skill/SKILL.md是面向 Agent 的操作技能文档(怎么调、参数、返回、铁律、陷阱),README.md是面向开发/评审的工程文档(定位、表、结构、安装、流程)。- 同步规则(改一处必改另一处):
- 新增/删除/改名契约端点 → 同步 README §3 表格 + SKILL 端点清单 +
__init__.py/init.py/load_path.py; - 表结构或唯一约束变更 → 同步 README §2 + SKILL 数据契约段 +
models/*.json+ DDL; - GD 顶层键或
canonical规则变更 → 同步 README §4/§7 + SKILL 确定性铁律段 + 自检脚本断言(第 4/5 组); - 新增陷阱/退回条款 → 同步 README §8 + SKILL「陷阱」节。
- 新增/删除/改名契约端点 → 同步 README §3 表格 + SKILL 端点清单 +
- 自查:
grep -c 'pbl_' skill/SKILL.md README.md(两侧端点名应一一对应,无单侧独有)。
10. 上下游依赖清单
上游(本模块依赖)
| 依赖 | 用途 | 缺失影响 |
|---|---|---|
pbl_common |
租户上下文、DB 适配(sql_rows/sql_exec/tenant_crud)、错误码、审计写入、CRUD 工厂 |
无法取租户/无法落库/无审计,编译不可用 |
pbl_blueprint |
蓝图聚合根 + 7 类子对象 + blueprint_version(编译输入源) |
③ 加载快照失败,全流程 fail-closed |
pbl_validation |
14 维校验结论与 quality_state(⑤ 质量门禁输入) |
无法判定质量门禁,拒绝编译 |
pbl_appcodes |
枚举编码注入(状态/触发方式/能力类别等字典) | 状态码不合法,前端展示异常 |
宿主应用 ServerEnv |
get_module_dbname('pbl_compiler') 库名映射、契约注册环境 |
触发硬编码 DB 名禁项 |
基础模块 sqlor / rbac |
标准 SQL API(仅 sor.C/U/D/R/I/sqlExe)、权限路径注册 |
数据访问与鉴权不可用 |
下游(依赖本模块)
| 模块 | 消费内容 |
|---|---|
pbl_agent_runtime(M4a/M4b) |
读 GD 作为 Designer/Critic Agent 的世界约束;调 preview 试编译 |
pbl_evidence(M5a/M5b) |
以 gd_id / content_fingerprint 关联产出物证据 |
pbl_assessment(M6) |
按 GD 的 rubric/目标结构做加权评估 |
pbl_scense_ext(M9)/ scense_game |
前端按 GD 渲染可玩场景(GD 是唯一渲染契约) |
pbl_runtime_ext(M11a/M11b) |
运行时事件与状态写入以 GD 定义的实体/规则为准 |
pbl_kdb_ext(M7) |
只读引用 GD 指纹做匿名聚合口径对齐 |
契约稳定性承诺
GD 的 10 个顶层键与 content_fingerprint 算法是跨模块契约,属破坏性变更范围:任何改动必须升 gd_version / compiler_version、同步全部下游模块、并重跑 §6 自检 6 组断言。