feat: architecture conformance checks in CI and metrics feedback loop

- cloud/arch_conformance.py: contract-version (+1 only, approved
  change record), forbidden-directory (own dir only, .. and symlink
  escapes resolved), forbidden-import layer bans audited against the
  baseline (foundation/execution/driver, server sqlite3 Row adapters
  deliberately allowed), G2 frozen-stack swap guard; skill-project
  one-call check.
- cloud/metrics.py: first-pass/defect/rework rates over terminal
  tasks, unknown rates as None (never 0), #212 dual-column per-task
  cost (total None when incomplete), pure feedback rules
  (tighten/route-with-extra-audit/recalibrate, auto_apply False),
  golden-set gate: unapproved sample set is never decision-ready.
- GET /metrics-report (authenticated, member-scoped, same as
  /metrics): attempts/defects are None until an attempt ledger
  exists, so rates stay unknown rather than fabricated.
- Tests: 29 arch + 15 metrics + 3 endpoint, each check with
  positive and negative cases; repo-level scan pins zero layer
  violations on the baseline so a future violation fails CI via the
  existing host-verify discover run (no workflow change).
This commit is contained in:
host committed 2026-11-04 20:55:49 +00:00
1 parent 4c38e3c452
commit 38a10db0c8
7 files changed
+1201

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
# 架构一致性进 CI 与指标闭环(审计加固第 5 步)
实现:`cloud/arch_conformance.py`(纯检查,标准库)、`cloud/metrics.py`
(纯计算,标准库)、端点 `GET /metrics-report`(`cloud/server.py`,
与 `/metrics` 同款认证与成员范围)。测试失败即 CI 失败:现有
`host-verify` 工作流已跑 `discover -s cloud/tests`,本轨未改任何
workflow——第 2 轨拥有 lint/类型/安扫,本轨不碰。
## 架构一致性检查(规则、理由、测试)
| 检查 | 规则 | 理由 | 测试 |
|---|---|---|---|
| 合同版本 | 冻结后版本只能 +1,且须有协调员批准的变更记录(声明向后兼容、列出受影响客户端;破坏性变更须有回归任务) | 契约静默漂移是并行线程分叉的根源;协议是唯一合法路径,检查执行协议而非只记录协议 | `test_arch_conformance.py::TestContractVersion`(正向:等值/批准+1/破坏性+回归任务;负向:未批准、无记录、跳号、不可解析) |
| 禁目录 | 任务只能写自有目录;`..` 与符号链接逃逸在解析后判定 | 跨目录写会 corrupt 他人 worktree | `TestDirectoryGuard`(正向:目录内;负向:`..`、symlink、混合写只报违规项)。口径复用已有定义,不另造:控制面 = 租户 work 目录(`worker._tenant_dirs` 的 `work`),技能项目 = `apps/<client>/` |
| 禁 import | 基础层(db/models/queue/logging_config)不 import 编排层;执行层(sandbox)不 import 持久化/编排层;除 db/backup/migrations 外任何 cloud 模块不得直接 `sqlite3.connect` 或 import `psycopg*`(服务层走 `cloud.db.connect` 或 Store) | 基础层上引是循环依赖前兆;执行器须可换、可假 runner 测试;驱动直连绕过 sqlite/postgres 唯一分发点 | `TestLayerImports`(每项正向+负向)+ 仓库级 `TestRepoLevelScan`(当前主线零违规,未来违规即 CI 失败)。有意不禁:`server.py` 的 `sqlite3.Row`/异常类型适配(那是在 `cloud.db` 连接上的适配,不是绕过;禁它会在基线误报) |
| 技术栈冻结 | G2 冻结表变更/删除须有批准的变更记录;新增客户端是新增不是换栈,通过 | 共享核心按冻结表构建,中途换栈使其静默失效 | `TestFrozenStack`(正向:不变/新增;负向:换栈/删除无批准) |
技能项目一键版:`check_skill_project` 把合同版本、冻结栈、目录写
三项合在一起验,`TestSkillProjectFixture` 用合成项目目录正向/负向
各验一次。静态 AST 扫描看不到 `importlib` 动态导入——这是已知
边界,不声称覆盖。
## 指标采集与报告
端点:`GET /metrics-report`(需 X-API-Key,成员范围)。报告格式
见 `cloud/metrics.py::build_report`:`golden_set`、`decision_ready`、
`metrics`、`thresholds` + `thresholds_source`、`feedback_actions`、
`feedback_consumers`。
指标口径(`compute_metrics`):
- 首过绿灯率 = 首过成功任务 / 已知 attempts 的终态任务;缺陷率 =
有缺陷任务 / 已知 defects 的终态任务;返工率 = attempts>1 任务 /
已知 attempts 的终态任务。终态 = completed/merged/failed/
merge-failed;`interrupted` 不算终态(平台中断自动重跑,算失败
是罚平台事件)。
- 每任务成本复用 #212 双列口径:有价 USD micros 与无价 actual
token 分列;成本不完整(无成本行或有无价 token)时
`total_usd_micros` 为 `None`,有价部分只以
`priced_usd_micros_partial` 出现——无价永不记 0、永不折进总额。
- 未知率回 `None` + `*_known: false`,绝不报 0。
当前诚实边界(端点实测):tasks 表无 attempts/defects 列,端点
传 `None`,首过/缺陷/返工率恒为 `None`;金任务集未经人工审定,
报告恒为 `pending_human_approval`、`decision_ready: false`。接真实
输入需两项前置(见待审定清单),本轨不编造。
## 回流与审计标准迭代(可测试规则,非口号)
`evaluate_feedback` 是纯函数,只产出 `auto_apply: false` 的行动
记录,不自动改路由、不自动改标准;行动由两处消费:
- 路由:`tighten_check`(某类缺陷率 > 阈值)→ 该类任务路由时加
一道审计门;`route_with_extra_audit`(首过率 < 下限)→ 全量加
审计门。消费动作本身须经协调员/人工确认后才生效。
- 审计标准:`tighten_check` / `recalibrate_check`(某检查的违规被
复核推翻率 > 阈值)各开一条标准复核项;标准文档本身的版本
变更走同一合同变更协议(`check_contract_version`,批准 +1),
测试 `TestGoldenSetAndStandardsLoop` 钉住“无批准改标准失败、
有批准通过”。误报复核规则:推翻率高只触发“复核阈值”行动,
绝不让检查自动放宽自己。
默认阈值(`DEFAULT_THRESHOLDS`:缺陷率 0.20、首过下限 0.50、
推翻率 0.30)只是测试用初值,报告标注
`thresholds_source: "default_unapproved"`;生产阈值须人工拍板后
以 `thresholds_source: "human_approved"` 传入,见待审定清单。