# pbl_domain_ext — PBL 基础域薄扩展(M8) world / scene / entity 三模块的 **PBL 侧薄扩展**:为复用平台的世界/场景/实体叠加 租户 / 蓝图 / 班级 / 团队关联维度。**不改三张基表结构、不侵入复用模块代码**(Q-OPEN-3 裁决:零改表)。 ## 核心事实 - **自有表 1 张**:`pbl_domain_ref`(`UNIQUE(tenant_id, ref_type, ref_id)`) - 权威 DDL:`projects/pbls/docs/01-design/data-model.md` §J1 - 表总账:`projects/pbls/pbls_spec.json` → `tables_by_module.pbl_domain_ext = 1`、`tables_total = 36` - **契约 13 个**:设计 `projects/pbls/docs/01-design/modules/pbl_domain_ext.md` §3.1(5) + §3.2(5) + §3.3(3) - **复用基表只读**:world / scene / entity 零 ALTER、零写入 - **他模块表只读**:`pbl_governance.pbl_team_member`(取团队成员,表缺失时降级空列表) ## 目录结构 ``` pbl_domain_ext/ ├── pbl_domain_ext/ # Python 包(模块名 = 包目录名,无 app.py) │ ├── __init__.py # ② 导出 load_pbl_domain_ext + 13 契约 │ ├── init.py # ③ load_pbl_domain_ext():注册 env.pbl_* + 设计原名;OWN_TABLES │ ├── api.py # ① 13 个契约接口实现(设计 §3) │ ├── base.py # 复用基表只读投影层(列名运行时探测、悬挂引用过滤) │ ├── db.py # 数据访问适配(SqlorAdapter 生产 / SqliteAdapter 测试) │ └── errors.py # PBL_E_* 错误码 + 统一异常(优先复用 pbl_common) ├── models/pbl_domain_ref.json # 表定义四段式(summary 数组 / fields / indexes / codes) ├── json/pbl_domain_ref.json # CRUD 浏览定义(tblname + params.browserfields/editable) ├── sql/pbl_domain_ext.sql # 建表 DDL(对齐 data-model.md §J1) ├── wwwroot/ │ ├── index.ui # 模块入口页(三组契约卡片导航) │ └── api/*.dspy # 13 个契约端点薄封装(与 api.py 一一对应) ├── scripts/load_path.py # RBAC 路径显式注册(13 dspy + 1 ui,禁通配符) ├── tests/ # 离线 sqlite 测试(62 用例,覆盖 13 契约 + 薄扩展铁律) ├── skill/SKILL.md # Agent 必读规范(铁律 + 陷阱 + 契约映射) ├── pyproject.toml └── README.md ``` ## 契约清单(设计 §3 → 实现 → 端点) | # | 接口 | 实现 | dspy 端点 | |---|---|---|---| | 1 | `bind_ref` | `api.py:bind_ref` | `api/pbl_domain_ref_bind.dspy` | | 2 | `unbind_ref` | `api.py:unbind_ref` | `api/pbl_domain_ref_unbind.dspy` | | 3 | `get_ref` | `api.py:get_ref` | `api/pbl_domain_ref_get.dspy` | | 4 | `list_refs` | `api.py:list_refs` | `api/pbl_domain_ref_list.dspy` | | 5 | `update_ref` | `api.py:update_ref` | `api/pbl_domain_ref_update.dspy` | | 6 | `list_worlds_by_tenant` | `api.py:list_worlds_by_tenant` | `api/pbl_world_list_by_tenant.dspy` | | 7 | `list_scenes_by_world` | `api.py:list_scenes_by_world` | `api/pbl_scene_list_by_world.dspy` | | 8 | `list_entities_by_scene` | `api.py:list_entities_by_scene` | `api/pbl_entity_list_by_scene.dspy` | | 9 | `get_world_with_pbl_context` | `api.py:get_world_with_pbl_context` | `api/pbl_world_get_context.dspy` | | 10 | `check_ref_access` | `api.py:check_ref_access` | `api/pbl_domain_ref_check_access.dspy` | | 11 | `list_teams_by_class` | `api.py:list_teams_by_class` | `api/pbl_team_list_by_class.dspy` | | 12 | `bind_team_to_world` | `api.py:bind_team_to_world` | `api/pbl_team_bind_world.dspy` | | 13 | `get_team_worlds` | `api.py:get_team_worlds` | `api/pbl_team_world_list.dspy` | ## 挂载 ```python from pbl_domain_ext import load_pbl_domain_ext load_pbl_domain_ext(env) # 注册 13 契约到 ServerEnv import sys; sys.path.append('modules/pbl_domain_ext/scripts') import load_path; load_path.register(env) # RBAC 路径注册(幂等) ``` 模块 host-agnostic:只依赖 sqlor / ahserver ServerEnv / 自有表,不依赖宿主入口与配置。 ## 验证 ```bash python3 -m py_compile pbl_domain_ext/*.py scripts/load_path.py tests/*.py python3 tests/test_domain_ref.py # 62 tests OK python3 scripts/load_path.py # 路径注册自检 OK ``` 详见 `skill/SKILL.md`(铁律、陷阱、需求追溯)与 `docs/work-log-2026-09-18.md`(本轮 QC 退回逐条整改记录)。