From ff667c58cd2be1c856d1e9bd01a0acc12b5d5f99 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 25 Aug 2026 21:27:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=8E=AF=E5=A2=83=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=94=AF=E4=B8=80=E4=BA=8B=E5=AE=9E=E6=BA=90=20+=20=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E8=84=9A=E6=89=8B=E6=9E=B6=20QC=20=E7=A1=AC=E9=97=A8?= =?UTF-8?q?=E7=A6=81=20+=20=E5=9F=BA=E7=A1=80=E6=A8=A1=E5=9D=97=20load=20?= =?UTF-8?q?=E5=8F=8D=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) project-directory-spec 新增第八章「部署环境信息(env/)」: - 唯一事实源 projects/{项目名}/env/{test,prod}.json;禁止 apps/ 下放部署凭据 (人事项目7 实测三份 env 矛盾:项目 9187 / 应用 9288+postgresql / 实际 mariadb) - 固定字段 schema;db.engine 必须与 DDL 方言一致 - 铁律:环境信息由人提供,agent 不得编造;禁「待明确」占位当交付;缺字段冒泡 need_info - PM 在 need_info 未答结时不得 review_rollback(回退上游解决不了外部输入缺口) 2) qc/review-develop 新增第六章「应用脚手架硬门禁」(原技能只有模块检查, 应用脚手架零检查项,Flask 因此两次过审): - 6.1 入口必须 ahserver,grep 命中 Flask/FastAPI/Django/jsonify 直接 reject - 6.2 基础模块 appbase/rbac 必须真正调 load_XXX(),只 import 标 loaded 属反模式 - 6.3 端口三处一致 6.4 DDL 方言与 engine 一致 6.5 依赖完整性 3) web-application-spec 补「基础模块只 import 不调 load_XXX()」反模式警示 --- .../all/web-application-spec/SKILL.md | 23 +++++++ .../common/project-directory-spec/SKILL.md | 50 +++++++++++++- .../roles/agent.qc/review-develop/SKILL.md | 66 +++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) diff --git a/skills_library/all/web-application-spec/SKILL.md b/skills_library/all/web-application-spec/SKILL.md index e7c2d82..cf8373d 100644 --- a/skills_library/all/web-application-spec/SKILL.md +++ b/skills_library/all/web-application-spec/SKILL.md @@ -117,6 +117,29 @@ if __name__ == '__main__': > > 不要给每个模块单独建 `app.py`、单独占端口、单独起服务——模块不是独立部署单元,应用才是。以上 8 步是应用一键部署正确执行的完整清单,缺一不可。 +> **🔴 反模式:基础模块只 import 不调 load_XXX()** +> +> `appbase` 和 `rbac` **同样有 `load_XXX()`**(`appbase/init.py: load_appbase()`、`rbac/init.py: load_rbac()`),必须像业务模块一样显式调用。只 `import` 包**不等于**已挂载。 +> +> ```python +> # ❌ 错误:只 import 就当成 loaded(rbac 权限/登录、appbase params 全没注册到 ServerEnv) +> for name in ["apppublic","sqlor","ahserver","appbase","rbac"]: +> pkg = importlib.import_module(name) +> status = "loaded" # ← 误报成功,掩盖问题 +> +> # ✅ 正确:import 后取 loader 并调用 +> from appbase.init import load_appbase +> from rbac.init import load_rbac +> env = ServerEnv() # ← 必须在 load_rbac() 之前 +> load_appbase(); load_rbac(); load_pybricks() +> ``` +> +> 症状:应用能起、`/healthz` 也可能 200,但登录必失败(rbac 未注册)、读 params 报错(appbase 未注册),日志却显示基础模块「loaded」。 +> +> 加载顺序固定:`ServerEnv()` → 基础模块(appbase/rbac/pybricks)→ 业务模块。基础模块晚于业务模块 load,业务模块拿不到权限/配置。 +> +> (2026-08-25 人事项目7 hrs7 实测:`load_base_modules()` 对 5 个基础模块只 `importlib.import_module` 就标 `status="loaded"`,而同文件的 `load_business_modules()` 却正确地 `getattr(pkg, loader)` + 调用——两套逻辑不一致,基础能力全部缺失。) + ### conf/config.json (Configuration) Must include the following sections: - `password_key`: System encryption key diff --git a/skills_library/pipelines/sdlc_general/common/project-directory-spec/SKILL.md b/skills_library/pipelines/sdlc_general/common/project-directory-spec/SKILL.md index 8e8f456..688b779 100644 --- a/skills_library/pipelines/sdlc_general/common/project-directory-spec/SKILL.md +++ b/skills_library/pipelines/sdlc_general/common/project-directory-spec/SKILL.md @@ -77,7 +77,7 @@ projects/{项目名}/ | 角色 | 产出 | 落点 | |------|------|------| | requirement | 需求规格说明书 | `projects/{项目名}/docs/00-requirement/requirement-spec.md` | -| requirement | 部署环境信息 | `projects/{项目名}/env/test.json` + `prod.json` | +| requirement | 部署环境信息(**引用+校验,不得编造**,见第八章) | `projects/{项目名}/env/test.json` + `prod.json` | | design | 系统架构 / UI 设计 | `projects/{项目名}/docs/01-design/architecture.md`、`ui-design.md` | | design | 模块级设计 | `projects/{项目名}/docs/01-design/modules/{模块名}.md` | | design | app spec.json | `projects/{项目名}/{应用名}_spec.json`(每个 app 一个) | @@ -106,3 +106,51 @@ projects/{项目名}/ ## 七、部署前设置远程仓库 应用部署测试前,机构 `apps/` 和 `modules/` 下应用使用到的仓库都要设置远程仓库(`git remote add origin <远程地址>`),否则部署时无法 git pull 拉取最新代码。 + +## 八、部署环境信息(env/)——唯一事实源与禁止编造 + +### 8.1 唯一存放位置(铁律) + +部署环境信息**只存放在项目目录** `projects/{项目名}/env/{test,prod}.json`,这是唯一事实源。 + +| 位置 | 允许内容 | 说明 | +|------|---------|------| +| `projects/{项目名}/env/{test,prod}.json` | **部署环境信息**:ssh / deploy / db / domain / port | ✅ 唯一事实源,deploy_test / deploy_prod 只读这里 | +| `apps/{应用名}/conf/config.json` | 应用**运行配置**:监听地址、日志级别、模块库名映射 | ✅ 应用自身配置 | +| `apps/{应用名}/env/` | —— | ❌ **禁止**。应用仓库不得存放部署环境信息,尤其禁止 ssh/db 凭据 | + +**禁止理由**:应用仓库会被多环境/多项目复用并推到远程,放部署凭据既泄密又造成多份互相矛盾的环境信息(2026-08-25 人事项目7 实测:`projects/hrs7/env/test.json` 全是「待明确」,而 `apps/hrs7/env/test.json` 自行写了 port 9288 + driver postgresql,与项目 env 的 9187 和测试机实际的 MariaDB 3306 三方矛盾,deploy_test 无法判断该信任哪份)。 + +### 8.2 字段 schema(固定,deploy 脚本按此读取) + +```json +{ + "app": "hrs7", + "env": "test", + "port": 9187, + "ssh": { "host": "...", "port": 22, "user": "...", "password": null, "key_file": null }, + "deploy": { "path": "~/hrs7_app", "repo_url": "...", "branch": "main" }, + "db": { "engine": "mariadb", "host": "...", "port": 3306, "user": "...", "password": "...", "dbname": "hrs7" }, + "domain": "..." +} +``` + +- `db.engine` 必须与应用 DDL 方言一致(mariadb → `BIGINT AUTO_INCREMENT`;postgresql → `BIGSERIAL`)。**engine 与 DDL 方言不符 = 部署必失败**,develop 阶段就要对齐。 +- 免密部署时 `ssh.password` 留 null,由部署角色注入机构密钥 `~/.ssh/org_keys/{org_id}/id_ed25519`。 + +### 8.3 环境信息由人提供,agent 不得编造(铁律) + +**环境信息(host / user / 凭据 / 部署路径 / 域名 / 端口)是外部事实,任何 agent 都无法自行推导。** + +- requirement 角色的职责是**引用与校验**,不是发明:把人/运维提供的环境信息落到 `env/{test,prod}.json`,并校验字段完整。 +- **禁止用「待明确 / 待确认 / TBD / placeholder」占位后当作已完成交付提交**。这类占位会一路流到 deploy_test 才暴露,浪费整条链。 +- 任一必填字段缺失 → **立即冒泡 need_info 问人**(不是 report_bug 报缺陷,也不是自己编一个),任务挂起等人回答,回答后再继续。 +- QC 检查 requirement 交付时:`env/{test,prod}.json` 存在占位词或 `pending` 数组非空 → 该项不算完成。 + +**必填字段清单**(缺任一即冒泡):`ssh.host`、`ssh.user`、`deploy.path`、`db.host`、`db.user`、`db.password`、`db.dbname`、`port`。 + +### 8.4 PM 不得在 need_info 未答结时回退上游 + +环境信息缺失属于**外部输入缺口**,回退到 requirement 重做**解决不了**——requirement 同样拿不到信息,只会再编一次占位,再被 QC 以「越权/占位」驳回,形成死循环(2026-08-25 人事项目7 实测:deploy_test 诚实冒泡 → 求助被驳 → PM 回退 requirement → requirement 再编 → QC 再驳)。 + +正确处理:存在未答结的 need_info 冒泡时,PM 只能**等待/催办**,不得 `review_rollback`。 diff --git a/skills_library/pipelines/sdlc_general/roles/agent.qc/review-develop/SKILL.md b/skills_library/pipelines/sdlc_general/roles/agent.qc/review-develop/SKILL.md index 44f872e..76b197c 100644 --- a/skills_library/pipelines/sdlc_general/roles/agent.qc/review-develop/SKILL.md +++ b/skills_library/pipelines/sdlc_general/roles/agent.qc/review-develop/SKILL.md @@ -8,6 +8,11 @@ capability: task_capability 被查对象:agent.develop。审查其产出时逐项机械检查,任一项不合格即 review_reject 并列出问题。 +**先判交付类型,再选检查章节**: +- 交付**业务模块**(Python 包,机构 `modules/{模块名}/`)→ 查第一~五章 +- 交付**应用脚手架**(机构 `apps/{应用名}/`,有 `app/{应用名}.py` 入口)→ 查**第六章**(硬门禁)+ 第四、五章 +- 两者都交 → 两套都查 + ## 一、模块目录结构(对照 module-development-spec) - 包目录 = 模块名(`{模块名}/{模块名}/`),不是 src/ @@ -40,3 +45,64 @@ capability: task_capability - 用 list_files / read_file 验证模块仓库目录结构真实存在 - 用 git_status 验证模块仓库有本地 git 提交(QC 核验「实际产出」的硬证据) - 用 run_shell 跑 py_compile / dspy 审计脚本验证语法 + +## 六、应用脚手架硬门禁(对照 web-application-spec)——任一不过即 reject + +交付 `apps/{应用名}/` 时逐条 grep 核验,**不看交付说明怎么写,只看代码实际内容**。 + +### 6.1 入口框架(最高优先级) + +```bash +grep -n 'from ahserver.webapp import webapp\|webapp(init)' apps/{应用名}/app/{应用名}.py # 必须命中 +grep -niE 'flask|fastapi|django|http\.server|jsonify|@app\.route' apps/{应用名}/app/*.py # 必须零命中 +grep -niE '^flask|^fastapi|^django' apps/{应用名}/requirements.txt # 必须零命中 +``` + +- 入口**必须** `from ahserver.webapp import webapp` + 末尾 `webapp(init)` +- **出现 Flask / FastAPI / Django / http.server / jsonify / @app.route 任一 → 直接 reject**,理由:bricks `.ui` 只有 ahserver 的 BricksUIProcessor 能渲染,用其它框架会导致模块界面全废、根路径只剩 JSON +- 已重复踩坑两次(2026-08-24 hrs6、2026-08-25 hrs7 均为 develop 用 Flask),这是复发项,务必先查 + +### 6.2 基础模块必须真正 load,不能只 import + +```bash +grep -n 'load_appbase\|load_rbac\|load_pybricks' apps/{应用名}/app/{应用名}.py # 必须都命中且是调用 +``` + +- 基础模块 **appbase / rbac** 也有 `load_XXX()`(`appbase/init.py: load_appbase()`、`rbac/init.py: load_rbac()`),**必须显式调用**: + +```python +from appbase.init import load_appbase +from rbac.init import load_rbac +env = ServerEnv() # ← 必须在 load_rbac() 之前 +load_appbase() +load_rbac() +load_pybricks() +# 再逐个 load 业务模块 +``` + +- **反模式(必 reject)**:只 `importlib.import_module(name)` 就把状态标成 `loaded`,没有 `getattr(pkg, "load_"+name)` 调用。后果:rbac 权限/登录、appbase params 全没注册到 ServerEnv,登录链路必崩,却误报挂载成功(2026-08-25 hrs7 实测:`load_base_modules()` 只 import,业务模块却正确调了 loader,两套逻辑不一致) +- 加载顺序:`ServerEnv()` → 基础模块 → 业务模块。基础模块晚于业务模块 load 会导致业务模块拿不到权限/配置 + +### 6.3 端口与环境信息一致性 + +- 应用端口**必须**取自 `projects/{项目名}/env/{test,prod}.json` 的 `port`,**不得**在应用仓库另定一个(应用 `env/` 目录本身就该不存在,见 project-directory-spec 第八章) +- 端口在项目 env、应用 conf、交付说明三处必须同值;出现两个及以上不同值 → reject + +### 6.4 DDL 方言与 db.engine 一致 + +```bash +grep -niE 'BIGSERIAL|SERIAL PRIMARY|nextval' apps/{应用名}/scripts/ddl/*.sql # engine=mariadb 时必须零命中 +``` + +- `env.db.engine=mariadb` → DDL 必须 `BIGINT AUTO_INCREMENT`,**禁 PostgreSQL 方言**(`BIGSERIAL`/`SERIAL`/`nextval`) +- `env.db.engine=postgresql` → 先确认目标机真有 PostgreSQL 在listen,否则 reject +- 2026-08-25 hrs7 实测:DDL 写 `BIGSERIAL`、env 写 `driver: postgresql:5432`,而测试机只有 MariaDB 3306(无 psql),部署必失败 + +### 6.5 依赖完整性 + +```bash +cat apps/{应用名}/requirements.txt # 必须含平台基础依赖 +``` + +- 必须包含应用实际用到的平台基础模块与 DB 驱动;只有 Web 框架而无 DB 驱动 → reject(连不上库) +- 2026-08-25 hrs7 实测:requirements.txt 只有 `Flask/Jinja2/Werkzeug`,既无 ahserver/sqlor/appbase/rbac,也无任何 DB 驱动