deliver: 交付收口(引擎代为提交)
This commit is contained in:
parent
34bdbd4af5
commit
71f83650f0
198
README.md
198
README.md
@ -1,2 +1,198 @@
|
||||
# pbl_kdb_ext
|
||||
# pbl_kdb_ext —— PBL 知识库(KDB)只读桩扩展模块(M7)
|
||||
|
||||
## 1. 模块用途
|
||||
|
||||
`pbl_kdb_ext` 是 PBL(Project/Problem-Based Learning)Agent OS 的**知识库只读扩展层**,为
|
||||
Designer / Critic Agent 与教师端提供「查得到、改不了」的知识条目检索与研究数据聚合能力。
|
||||
|
||||
设计定位(对应两个开放问题的当期落地口径):
|
||||
|
||||
| 开放问题 | 当期口径 | 本模块实现 |
|
||||
|---------|---------|-----------|
|
||||
| **Q-OPEN-5** KDB 是否落地向量库 | 暂不引入向量库/Embedding | **不落地向量桩**:检索走结构化字段等值 + `LIKE` 关键词匹配,返回体带 `stub` 标记,后续可平滑替换为向量检索而不改契约 |
|
||||
| **Q-OPEN-6** research 数据是否可写回 | 只读、匿名 | **只读匿名聚合**:分组统计评估分数,最小分组 `k_min=5`,样本不足的分组直接抑制(`suppressed_groups` 计数),返回 `anonymous=true / written=false` |
|
||||
|
||||
### G6 范围纪律(硬约束,可 grep 核对)
|
||||
|
||||
本模块**零写入业务对象**,明确**不实现**以下任何能力:
|
||||
|
||||
- ❌ `kdb.write` / `kdb.add_candidate` / `kdb.propose_pattern`(知识条目写入、候选入库、模式提炼)
|
||||
- ❌ `research.write`(研究数据写回)
|
||||
- ❌ `sim.*`(仿真/模拟相关全部接口)
|
||||
|
||||
`pbl_kdb_item.is_write_locked` 恒为 `1`,模块内**不存在**任何 `UPDATE` / `INSERT` / `DELETE`
|
||||
`pbl_kdb_item` 的代码路径;唯一的写操作是**查询留痕日志** `pbl_kdb_query_log`(审计用途,
|
||||
append-only,不属于业务对象写入)。
|
||||
|
||||
## 2. 数据表(2 张,定义在 `models/`)
|
||||
|
||||
### 2.1 `pbl_kdb_item` —— 知识条目表(只读,写锁定)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | str(32) | 主键 |
|
||||
| `tenant_id` | str(32) | 租户隔离,**所有查询强制打头** |
|
||||
| `code` | str(64) | 条目业务编码(唯一,配合 tenant_id) |
|
||||
| `item_type` | str(32) | 条目类型(pattern / case / rubric_ref 等,取自 appcodes) |
|
||||
| `title` | str(255) | 标题(检索字段) |
|
||||
| `summary_txt` | text | 摘要正文(`LIKE` 关键词匹配字段) |
|
||||
| `payload_json` | text | 结构化载荷(JSON,读取时反序列化为 dict) |
|
||||
| `source` | str(64) | 来源(可作过滤维度) |
|
||||
| `version_no` | int | 版本号 |
|
||||
| `status` | str(16) | 状态,检索仅命中 `published` |
|
||||
| `is_write_locked` | short | **恒为 1**:写锁定标记,本模块只读约束的数据层体现 |
|
||||
| `created_at` / `updated_at` | timestamp | 审计时间戳 |
|
||||
|
||||
> 表数据由上游(蓝图编译 / 治理域)产生并置 `is_write_locked=1`;本模块只消费,不生产。
|
||||
|
||||
### 2.2 `pbl_kdb_query_log` —— 检索留痕日志表(append-only)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | str(32) | 主键 |
|
||||
| `tenant_id` | str(32) | 租户隔离 |
|
||||
| `query_uid` | str(64) | 查询唯一标识(`Q` + sha256(参数+时间) 前 20 位) |
|
||||
| `caller_type` | str(16) | 调用方类型(agent / teacher,缺省 `agent`) |
|
||||
| `caller_id` | str(32) | 调用方 ID(取自 `actor_id()`) |
|
||||
| `query_json` | text | 查询参数快照(剔除 `caller_id`,避免重复存储) |
|
||||
| `hit_count` | int | 命中条数 |
|
||||
| `latency_ms` | int | 检索耗时(毫秒) |
|
||||
| `ok` | short | 是否成功(恒 1,异常路径直接抛错不落日志) |
|
||||
| `created_at` | timestamp | 记录时间 |
|
||||
|
||||
用途:检索行为可观测(命中率 / 延迟)与审计追溯。**只 INSERT,不 UPDATE / DELETE。**
|
||||
|
||||
### 2.3 跨模块只读依赖
|
||||
|
||||
`pbl_research_aggregate` 只读聚合 **`pbl_assessment_record`**(属 `pbl_assessment` 模块,
|
||||
M6 产出)的 `total_score` / `band` / `assessor_type` / `blueprint_id` 字段——**跨模块只读,
|
||||
不写不建表**,符合交互/扩展层模块的数据所有权边界。
|
||||
|
||||
## 3. 契约接口(3 个,全部只读)
|
||||
|
||||
三个契约均为 `wwwroot/api/*.dspy` 薄封装 → 调用 `pbl_kdb_ext/api.py` 中的同名 async 函数
|
||||
(函数经 `load_pbl_kdb_ext()` 注册到 ServerEnv,dspy 内直接以全局名调用,无需 import)。
|
||||
|
||||
| # | 契约名 | HTTP 路径(自动路由) | 入参 | 返回要点 |
|
||||
|---|--------|---------------------|------|---------|
|
||||
| 1 | `pbl_kdb_search` | `/pbl_kdb_ext/api/pbl_kdb_search.dspy` | `item_type`、`source`、`keyword`、`limit`(≤50,默认 10)、`caller_type` | `{ok, data[], hit_count, latency_ms, writable:false, stub}`;固定过滤 `tenant_id` + `is_write_locked=1` + `status='published'`;写一条 `pbl_kdb_query_log` |
|
||||
| 2 | `pbl_kdb_get` | `/pbl_kdb_ext/api/pbl_kdb_get.dspy` | `id` 或 `code` | `{ok, data{...payload_json 已反序列化}, is_write_locked:true, stub}`;未命中抛 `PBL_KDB_NOT_FOUND` |
|
||||
| 3 | `pbl_research_aggregate` | `/pbl_kdb_ext/api/pbl_research_aggregate.dspy` | `group_by` ∈ {`band`, `assessor_type`, `blueprint_id`}(默认 `band`) | `{ok, dimension, groups[{group,n,avg_score,min_score,max_score}], suppressed_groups, k_min:5, anonymous:true, written:false, note}`;维度非法抛 `PBL_AGG_DIM_INVALID` |
|
||||
|
||||
错误码沿用 `pbl_common` 统一错误体系(`PblError`):`PBL_KDB_NOT_FOUND`、`PBL_AGG_DIM_INVALID`。
|
||||
本模块**不另设** `errors.py`——错误类型与错误码常量由公共内核 `pbl_common.api` 统一提供,
|
||||
模块内只做抛出,避免与公共内核重复定义(交付清单与磁盘文件一一对应)。
|
||||
|
||||
## 4. 目录结构
|
||||
|
||||
```
|
||||
pbl_kdb_ext/
|
||||
├── pbl_kdb_ext/ # Python 包(包目录 = 模块名)
|
||||
│ ├── __init__.py # ② 导出三个契约函数(三处同步之②)
|
||||
│ ├── init.py # ③ load_pbl_kdb_ext():env.<契约> = <契约>
|
||||
│ └── api.py # ① 契约实现(只读检索 / 详情 / 匿名聚合)
|
||||
├── wwwroot/
|
||||
│ ├── index.ui # 模块入口页(三张卡片 → urlwidget 到内容区)
|
||||
│ └── api/
|
||||
│ ├── pbl_kdb_search.dspy
|
||||
│ ├── pbl_kdb_get.dspy
|
||||
│ └── pbl_research_aggregate.dspy
|
||||
├── models/
|
||||
│ ├── pbl_kdb_item.json # 表定义(四段式 summary/fields/indexes/codes)
|
||||
│ └── pbl_kdb_query_log.json
|
||||
├── json/ # CRUD 定义(只读浏览用,无 editable 写入 URL)
|
||||
│ ├── kdb_ext_pbl_kdb_item.json
|
||||
│ └── kdb_ext_pbl_kdb_query_log.json
|
||||
├── scripts/load_path.py # RBAC 路径注册(显式枚举,无通配符)
|
||||
├── sql/pbl_kdb_ext.sql # DDL(建表脚本)
|
||||
├── skill/SKILL.md # Agent 必读模块技能文档
|
||||
├── pyproject.toml
|
||||
└── README.md # 本文件
|
||||
```
|
||||
|
||||
## 5. 挂载方式(宿主应用集成)
|
||||
|
||||
模块**不是独立部署单元**——无自己的 `app.py` / 端口 / Dockerfile / 服务脚本,只能由宿主应用
|
||||
(`apps/pbls`)挂载:
|
||||
|
||||
```python
|
||||
# apps/pbls/app/pbls.py 的 init() 中,ServerEnv() 与基础模块加载之后:
|
||||
from pbl_kdb_ext.init import load_pbl_kdb_ext
|
||||
load_pbl_kdb_ext() # 返回 'pbl_kdb_ext'
|
||||
```
|
||||
|
||||
`load_pbl_kdb_ext()` 是**唯一集成点**,做且只做一件事:把三个契约函数注册到 `ServerEnv`
|
||||
(`env.pbl_kdb_search` / `env.pbl_kdb_get` / `env.pbl_research_aggregate`),使 `.dspy` / `.ui`
|
||||
能以全局名直接调用。宿主决定何时、以何顺序挂载。
|
||||
|
||||
### 5.1 三处同步铁律
|
||||
|
||||
新增/删除一个契约函数必须同步改三处,缺一即运行期报错:
|
||||
|
||||
1. `pbl_kdb_ext/api.py` —— 函数实现;
|
||||
2. `pbl_kdb_ext/__init__.py` —— `from .api import <fn>` 导出(缺 → ImportError / AttributeError);
|
||||
3. `pbl_kdb_ext/init.py` —— `env.<fn> = <fn>`(缺 → dspy 内 `NameError: name '<fn>' is not defined`)。
|
||||
|
||||
核对命令:`grep -rn 'pbl_kdb_search' pbl_kdb_ext/ --include='*.py'` 应恰好命中这三处。
|
||||
|
||||
### 5.2 RBAC 注册(`scripts/load_path.py`)
|
||||
|
||||
由 `apps/pbls/build.sh` 调用 `register()`,逐条注册(**显式枚举,禁止 `%` / `*` 通配符**):
|
||||
|
||||
| 路径 | 角色 |
|
||||
|------|------|
|
||||
| `/pbl_kdb_ext` | `logined` |
|
||||
| `/pbl_kdb_ext/index.ui` | `logined` |
|
||||
| `/pbl_kdb_ext/api/pbl_kdb_search.dspy` | `logined` |
|
||||
| `/pbl_kdb_ext/api/pbl_kdb_get.dspy` | `logined` |
|
||||
| `/pbl_kdb_ext/api/pbl_research_aggregate.dspy` | `logined` |
|
||||
|
||||
入口页的**目录路径与文件路径都要注册**,否则入口页上线即 403。`set_role_perm.py` 不在位时
|
||||
脚本打印 `PENDING <role> <path>` 清单并以退出码 1 返回(不静默跳过、不抛异常),由部署角色
|
||||
手工补注册。本模块全部接口为登录可读,无 `any`(匿名)路径,也无 teacher/admin 专属写路径
|
||||
(因为没有写接口)。
|
||||
|
||||
### 5.3 数据库名
|
||||
|
||||
模块内**不硬编码库名**,统一通过 `pbl_common` 的 DB 适配层(`sql_rows` / `sql_exec` 的
|
||||
`'pbl'` 逻辑库标识)由宿主应用的 `get_module_dbname()` 解析到真实库。
|
||||
|
||||
## 6. 只读 / 匿名聚合约束说明(验收要点)
|
||||
|
||||
1. **零写入**:模块内无 `kdb.write` / `research.write` / `sim.*` 任何实现;
|
||||
`grep -rn "def .*write\|def .*sim_" pbl_kdb_ext/` 结果为空。
|
||||
2. **写锁定**:`pbl_kdb_search` 的 WHERE 固定包含 `is_write_locked = 1`;`pbl_kdb_get`
|
||||
返回体固定 `is_write_locked: true`;无任何路径可修改该字段。
|
||||
3. **租户隔离**:三个契约的 SQL 全部以 `tenant_id = ${t}$` 打头(`tenant_id()` 取自
|
||||
`pbl_common` 请求上下文),缺失租户上下文即报错,不降级为全租户查询。
|
||||
4. **匿名聚合(隐私线)**:`k_min = 5`;分组样本数 `< 5` 的分组**不出现在 `groups` 中**,
|
||||
仅计入 `suppressed_groups`;返回体不含任何个体标识(无 student_id / user_id / 明细行)。
|
||||
5. **聚合维度白名单**:`group_by` 仅允许 `band` / `assessor_type` / `blueprint_id`,
|
||||
其它值抛 `PBL_AGG_DIM_INVALID`(同时杜绝 SQL 注入——维度名不来自自由文本拼接)。
|
||||
6. **参数化 SQL**:全部使用 sqlor `${name}$` 占位符,无字符串拼接用户输入。
|
||||
7. **桩标记透明**:检索/详情返回体带 `stub: 'kdb_stub: no vector store (Q-OPEN-5)'`,
|
||||
调用方可据此判断结果来自结构化匹配而非语义向量检索。
|
||||
8. **可观测**:每次检索落一条 `pbl_kdb_query_log`(hit_count / latency_ms / query_json),
|
||||
用于命中率与延迟分析;日志表 append-only。
|
||||
|
||||
## 7. 依赖
|
||||
|
||||
- **基础/公共**:`pbl_common`(租户上下文、DB 适配、`PblError` 错误码、`actor_id` /
|
||||
`now_str` / `json_dump` 工具)、`ahserver`(ServerEnv)、`sqlor`(数据访问)。
|
||||
- **数据依赖(只读)**:`pbl_assessment` 的 `pbl_assessment_record`(聚合数据源)。
|
||||
- **被依赖**:`pbl_agent_runtime`(Designer/Critic Agent 检索知识条目)、
|
||||
`pbl_scense_ext` / 教师端页面(研究数据聚合展示)。
|
||||
|
||||
## 8. 本地校验
|
||||
|
||||
```bash
|
||||
cd modules/pbl_kdb_ext
|
||||
python3 -m py_compile pbl_kdb_ext/*.py scripts/load_path.py # .py 编译
|
||||
python3 -c "import json;[json.load(open('models/'+f)) for f in ('pbl_kdb_item.json','pbl_kdb_query_log.json')]"
|
||||
python3 -c "import json;json.load(open('wwwroot/index.ui'));print('index.ui JSON OK')"
|
||||
grep -rn "^import\|^from" wwwroot/ --include='*.dspy' # dspy 审计:应为空
|
||||
grep -rn "entire_url(" wwwroot/index.ui | grep -v "/pbl_kdb_ext/" # 绝对路径核对:应为空
|
||||
```
|
||||
|
||||
> `.dspy` 不能用 `py_compile` 校验(框架运行期注入 async 函数体,顶层 `return`/`await` 合法),
|
||||
> 只做 grep 审计。
|
||||
|
||||
52
docs/work-log-2026-09-18.md
Normal file
52
docs/work-log-2026-09-18.md
Normal file
@ -0,0 +1,52 @@
|
||||
# pbl_kdb_ext 工作日志 —— 2026-09-18(QC 退回重做轮)
|
||||
|
||||
## 范围 / 背景
|
||||
|
||||
- 仓库/模块:机构工作空间 `modules/pbl_kdb_ext`(PBL Agent OS,项目 `projects/pbls`)
|
||||
- 任务:`[M7] pbl_kdb_ext KDB 只读桩扩展`(pipeline_tasks.id = `OgtZuajAdd0y65BZdv8jr`,
|
||||
iteration `pbls-初始迭代`,task_kind = new_dev → 本轮为 QC 退回后的重做修复)
|
||||
- 本轮目标:逐条闭环 agent.qc 的 5 条退回意见,不扩大范围(G6 范围纪律:仍零写入,
|
||||
不新增 kdb.write / research.write / sim.*)。
|
||||
|
||||
## QC 退回意见 → 处理结果(逐条)
|
||||
|
||||
| # | 位置 | 问题 | 处理 |
|
||||
|---|------|------|------|
|
||||
| 1 | `scripts/load_path.py` PATHS | 仅注册 3 个 `.dspy`,缺 `/pbl_kdb_ext` 与 `/pbl_kdb_ext/index.ui`,入口页上线即 403 | **已修**:PATHS 补两条 `logined` 记录(目录路径 + index.ui 文件路径),共 5 条,仍显式枚举无通配符 |
|
||||
| 2 | `wwwroot/index.ui` 三处 `binds.options.url` | `{{entire_url('api/pbl_kdb_search.dspy')}}` 为相对路径,可能丢模块前缀 → RBAC 403(module-development-spec 2.3c) | **已修**:三处全部改为绝对路径 `{{entire_url('/pbl_kdb_ext/api/xxx.dspy')}}`(search / get / aggregate) |
|
||||
| 3 | `scripts/load_path.py` 第 38 行 | `print(' PENDING %%-12s %s' %(role, path))` —— `%%` 转义后格式串只剩 1 个 `%s` 却传 2 个参数,出现 pending 必抛 TypeError,注册失败时脚本崩溃而非打印清单 | **已修**:改为 `print(' PENDING %-12s %s' % (role, path))`;同时给 `subprocess.call` 加 `try/except OSError`(CLI 不存在时归入 pending 而非崩溃),summary 行补上 MODULE 名 |
|
||||
| 4 | `README.md` | 全文仅 15 字节 `# pbl_kdb_ext`,boilerplate-only(spec 明令禁止) | **已修**:重写为 8 节完整文档——模块用途(Q-OPEN-5/6 口径 + G6 范围纪律)、2 张表字段说明(pbl_kdb_item / pbl_kdb_query_log)+ 跨模块只读依赖、3 个契约接口(路径/入参/返回/错误码)、目录结构、挂载方式(load_pbl_kdb_ext + 三处同步铁律 + RBAC 表 + 库名)、只读/匿名聚合 8 条验收约束、依赖、本地校验命令 |
|
||||
| 5 | 交付摘要清单 vs 磁盘 | 清单声称写入 `pbl_kdb_ext/errors.py`,实测不存在,声称与事实矛盾 | **已修(走"移除"分支)**:错误类型/错误码由公共内核 `pbl_common.api` 的 `PblError` 统一提供,本模块只抛出不另定义,故 `errors.py` **不是本任务应产出**,已从交付清单移除;README 第 3 节明确写出该决策,保证清单与盘上文件一一对应。`api.py` 的 import 闭包保持 `from pbl_common.api import PblError, ...` 不变,无悬空引用 |
|
||||
|
||||
## 关键技术决策
|
||||
|
||||
1. **errors.py 取舍**:两个合法分支(补写并纳入 import 闭包 / 从清单移除)中选**移除**。
|
||||
理由:`PblError` 与错误码体系属 `pbl_common` 公共内核职责,模块内再建 errors.py 会与
|
||||
公共内核重复定义、增加两处维护点;本模块仅 2 个错误码(`PBL_KDB_NOT_FOUND`、
|
||||
`PBL_AGG_DIM_INVALID`),直接在 api.py 抛出即可。
|
||||
2. **load_path.py 健壮性**:QC #3 的根因是"失败路径未被测过"。除修格式串外,补
|
||||
`try/except OSError`,使 `set_role_perm.py` 不在位时走 pending 打印分支(退出码 1),
|
||||
而不是抛 FileNotFoundError 让 build.sh 第 8 步整体中断。
|
||||
3. **index.ui 保持纯 JSON**:入口页三张卡片(VBox + binds click → `actiontype: urlwidget`
|
||||
→ `target: app.pbl_kdb_ext_content`,`mode: replace`),内容区 VBox 的 `id` 放在
|
||||
**widget 顶层**(不是 options 内),符合 bricks 规范;无 `<link>`/`<script>` 标签,
|
||||
无 fetch/setInterval。
|
||||
4. **范围纪律未破**:本轮只改 3 个文件(load_path.py / index.ui / README.md)+ 新增本日志,
|
||||
未触碰 api.py 的读写语义,未新增任何写入接口。
|
||||
|
||||
## 验证
|
||||
|
||||
- 静态:`python3 -m py_compile pbl_kdb_ext/*.py scripts/load_path.py`(.py 编译通过);
|
||||
`json.load` 校验 `wwwroot/index.ui` 与 `models/*.json` 合法性;
|
||||
`grep -rn "^import\|^from" wwwroot/ --include='*.dspy'` 为空(dspy 审计);
|
||||
`grep -rn "entire_url(" wwwroot/index.ui | grep -v "/pbl_kdb_ext/"` 为空(绝对路径核对);
|
||||
`grep -c "PENDING %-" scripts/load_path.py` = 1 且 `grep -c "%%" scripts/load_path.py` = 0。
|
||||
- 环境受限未跑:无可用 pbls 测试实例与 MySQL(`pbl_kdb_item` / `pbl_assessment_record`
|
||||
无数据),故未做 HTTP 200 端到端验证与聚合抑制(k_min=5)实测;RBAC 注册未实跑
|
||||
(`set_role_perm.py` 不在本工作空间),按设计走 PENDING 打印分支。此项留待 deploy_test。
|
||||
|
||||
## 当前状态
|
||||
|
||||
- 分支:`main`;本轮修改文件:`scripts/load_path.py`、`wwwroot/index.ui`、`README.md`、
|
||||
`docs/work-log-2026-09-18.md`(新增)。
|
||||
- 提交由 PM 审核通过后系统统一执行。
|
||||
@ -3,9 +3,12 @@
|
||||
"""pbl_kdb_ext RBAC 路径注册(硬门禁 6.6 / QC #11)。
|
||||
|
||||
约定:
|
||||
- 路径 = 模块自动路由 `/pbl_kdb_ext/api/<契约>.dspy`,不带端口、不带 /wss 前缀;
|
||||
- 角色 `logined` = 登录即可访问的读接口;写接口按角色分级(teacher/admin);
|
||||
- 由 apps/pbls/build.sh 第 8 步调用 `register()`;rbac CLI 不在位时打印清单(不静默跳过)。
|
||||
- 路径 = 模块自动路由 `/pbl_kdb_ext/...`,不带端口、不带 /wss 前缀;
|
||||
- 角色 `logined` = 登录即可访问的读接口(本模块零写入,无 teacher/admin 写接口);
|
||||
- 入口页与其目录路径必须同时注册(QC #1):`/pbl_kdb_ext` + `/pbl_kdb_ext/index.ui`,
|
||||
否则入口页上线即 403;
|
||||
- 禁止通配符(`%` / `*`)——每条路径显式列出(module-development-spec:load_path.py Wildcards FORBIDDEN);
|
||||
- 由 apps/pbls/build.sh 第 8 步调用 `register()`;rbac CLI 不在位时打印 PENDING 清单(不静默跳过、不崩溃)。
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
@ -13,28 +16,43 @@ import sys
|
||||
|
||||
MODULE = 'pbl_kdb_ext'
|
||||
|
||||
# (path, role)
|
||||
# (path, role) —— 显式枚举,无通配符
|
||||
PATHS = [
|
||||
# 入口页(目录路径 + 文件路径都要注册,QC #1)
|
||||
('/pbl_kdb_ext', 'logined'),
|
||||
('/pbl_kdb_ext/index.ui', 'logined'),
|
||||
# 三个只读契约接口
|
||||
('/pbl_kdb_ext/api/pbl_kdb_search.dspy', 'logined'),
|
||||
('/pbl_kdb_ext/api/pbl_kdb_get.dspy', 'logined'),
|
||||
('/pbl_kdb_ext/api/pbl_research_aggregate.dspy', 'logined'),
|
||||
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
"""逐条注册 RBAC 权限路径。
|
||||
|
||||
返回 True = 全部注册成功;False = 存在 pending(CLI 不在位/失败),
|
||||
此时打印待补清单供部署角色手工执行 set_role_perm.py,脚本本身不抛异常。
|
||||
"""
|
||||
tool = os.environ.get('RBAC_SET_PERM', 'set_role_perm.py')
|
||||
py = sys.executable if os.environ.get('PY') else 'python3'
|
||||
done, missing = 0, []
|
||||
for path, role in PATHS:
|
||||
if subprocess.call([sys.executable if os.environ.get('PY') else 'python3',
|
||||
tool, role, path],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0:
|
||||
try:
|
||||
rc = subprocess.call([py, tool, role, path],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
except OSError:
|
||||
rc = 1
|
||||
if rc == 0:
|
||||
done += 1
|
||||
else:
|
||||
missing.append((path, role))
|
||||
print('[%s] rbac paths: total=%d ok=%d pending=%d' %(len(PATHS), done, len(missing)))
|
||||
# QC #3:格式串占位符与实参个数必须一致(原 `%%-12s` 转义后只剩 1 个 %s 却传 2 个参数 → TypeError)
|
||||
print('[%s] rbac paths: total=%d ok=%d pending=%d'
|
||||
% (MODULE, len(PATHS), done, len(missing)))
|
||||
for path, role in missing:
|
||||
print(' PENDING %%-12s %s' %(role, path))
|
||||
print(' PENDING %-12s %s' % (role, path))
|
||||
return len(missing) == 0
|
||||
|
||||
|
||||
|
||||
@ -3,21 +3,34 @@
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "20px"
|
||||
"padding": "20px",
|
||||
"backgroundColor": "#F5F6FA"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "KDB 只读桩与匿名聚合(M7,零写入)",
|
||||
"fontSize": "24px"
|
||||
"label": "PBL 知识库只读桩(pbl_kdb_ext / M7)",
|
||||
"fontSize": "24px",
|
||||
"fontWeight": "bold",
|
||||
"color": "#1F2A44"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "零写入模块:pbl_kdb_item.is_write_locked 恒为 1;不提供 kdb.write / research.write / sim.* 任何写入路径;KDB 为不落地向量桩(Q-OPEN-5);research 仅只读匿名聚合,分组样本 < 5 自动抑制(Q-OPEN-6)。",
|
||||
"fontSize": "13px",
|
||||
"color": "#666666",
|
||||
"marginTop": "6px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {
|
||||
"gap": "16px",
|
||||
"minWidth": "250px"
|
||||
"minWidth": "250px",
|
||||
"marginTop": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
@ -25,7 +38,8 @@
|
||||
"options": {
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"padding": "20px",
|
||||
"cursor": "pointer"
|
||||
"cursor": "pointer",
|
||||
"borderRadius": "6px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
@ -34,7 +48,7 @@
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.pbl_kdb_ext_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('api/pbl_kdb_search.dspy')}}"
|
||||
"url": "{{entire_url('/pbl_kdb_ext/api/pbl_kdb_search.dspy')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
@ -43,7 +57,18 @@
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "pbl_kdb_search"
|
||||
"label": "KDB 只读检索",
|
||||
"fontSize": "16px",
|
||||
"fontWeight": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "pbl_kdb_search:结构化字段等值 + LIKE 关键词匹配(无向量库),返回 hit_count / latency_ms,写 pbl_kdb_query_log 留痕。",
|
||||
"fontSize": "12px",
|
||||
"color": "#666666",
|
||||
"marginTop": "6px"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -53,7 +78,8 @@
|
||||
"options": {
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"padding": "20px",
|
||||
"cursor": "pointer"
|
||||
"cursor": "pointer",
|
||||
"borderRadius": "6px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
@ -62,7 +88,7 @@
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.pbl_kdb_ext_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('api/pbl_kdb_get.dspy')}}"
|
||||
"url": "{{entire_url('/pbl_kdb_ext/api/pbl_kdb_get.dspy')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
@ -71,7 +97,18 @@
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "pbl_kdb_get"
|
||||
"label": "KDB 条目详情",
|
||||
"fontSize": "16px",
|
||||
"fontWeight": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "pbl_kdb_get:按 id / code 取单条(tenant_id 强制打头),返回 is_write_locked=true,不存在抛 PBL_KDB_NOT_FOUND。",
|
||||
"fontSize": "12px",
|
||||
"color": "#666666",
|
||||
"marginTop": "6px"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -81,7 +118,8 @@
|
||||
"options": {
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"padding": "20px",
|
||||
"cursor": "pointer"
|
||||
"cursor": "pointer",
|
||||
"borderRadius": "6px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
@ -90,7 +128,7 @@
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.pbl_kdb_ext_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('api/pbl_research_aggregate.dspy')}}"
|
||||
"url": "{{entire_url('/pbl_kdb_ext/api/pbl_research_aggregate.dspy')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
@ -99,7 +137,18 @@
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "pbl_research_aggregate"
|
||||
"label": "Research 匿名聚合",
|
||||
"fontSize": "16px",
|
||||
"fontWeight": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"label": "pbl_research_aggregate:按 band / assessor_type / blueprint_id 分组统计评估分数,k_min=5 以下分组抑制,anonymous=true、written=false。",
|
||||
"fontSize": "12px",
|
||||
"color": "#666666",
|
||||
"marginTop": "6px"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -112,7 +161,10 @@
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"flex": "1",
|
||||
"marginTop": "20px"
|
||||
"marginTop": "20px",
|
||||
"padding": "16px",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"borderRadius": "6px"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user