feat: 环境信息唯一事实源 + 应用脚手架 QC 硬门禁 + 基础模块 load 反模式

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()」反模式警示
This commit is contained in:
yumoqing 2026-08-25 21:27:10 +08:00
parent 520a6d4f89
commit ff667c58cd
3 changed files with 138 additions and 1 deletions

View File

@ -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 就当成 loadedrbac 权限/登录、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

View File

@ -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`

View File

@ -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 驱动