audit: wire pushback gate to hard merge gate and hash-chain ledger (gap 1)

Track 3 decisions (audit_pushback) now really block the server merge
endpoint and really land on the track 4 hash chain, via the new
cloud/audit_wiring.py translation layer:

- POST /tasks/{id}/merge ANDs the chain pushback block
  (merge_blocked/release_blocked/blocked_by=audit_pushback/round/
  failed_checks, replayed per module+contract version) with the
  hard gate's five checks in every gate mode; either side blocks
  -> 409, status unchanged, audit_pushback_blocked appended to
  merge_gate.blocks. Ledger reads that fail (other than a missing
  audit_ledger table) fail closed to 409.
- Every pushback/fix/re-audit/rebuild/escalation step is written
  to the ledger with track 3 fields authoritative and track 4
  storage authoritative; CONDITIONAL maps to fail at the boundary
  with the original preserved, history never rewritten.
- Hard-gate 409 blocks codes feed pushback packages machine-first
  (branch protection survives as its own extra check).
- Re-audit QA guard: a claimed pass with un-rerun or failed QA
  links never reaches the chain.
- Track 4 compat: one audit_wiring_event action for escalation /
  blocked-quota / audit-sample kinds, counted in the timeline.
- Resolved tension (documented in audit_wiring.py): a meta-review
  correction to fail re-blocks at once, but a correction to pass
  does NOT unblock by itself -- track 3 schedules a re-audit,
  it does not release the gate; track 4 failure counts still use
  effective verdicts, so the two views may disagree while a
  re-audit is pending.

Tests: cloud/tests/test_audit_wiring.py (18 items), incl. a real
HTTP end-to-end chain (fail -> 409 -> fix -> re-audit pass ->
200) and a separate QA-link chain test.
This commit is contained in:
host committed 2026-11-03 02:49:25 +00:00
1 parent eeb10118d6
commit 382ec6d62e
5 files changed
+1512 -12

No files matched your search

+17 -11
View File
@@ -696,9 +696,13 @@ tests/ # QA 线程专属,含跨端一致性用例
`release_blocked`、`blocked_by="audit_pushback"`、轮次、
失败检查清单),硬门禁自己的检查与推回阻塞是“与”关系:
审计通过不解硬门禁的锁,硬门禁通过也不解推回的锁,两边都
通过才能合并/发布。代码侧该接口目前是提案(第 1 轨落地时
确认接线,在此之前本模块本身不拦任何服务端点,这一点要
如实写进 PR 摘要,不冒充已接线)。
通过才能合并/发布。接线已落地(`cloud/audit_wiring.py`):
服务端 POST /tasks/{id}/merge 在五项检查之外再读链上推回
阻塞,任一阻塞即 409、状态不变;推回阻塞与门禁模式无关,
显式确认绝不覆盖它;每轮推回/修复/重审/推倒/升级都写进
第 4 轨哈希链账本(字段以本轨为准,第 4 轨加了
`audit_wiring_event` 兼容动作,见该模块)。CONDITIONAL
在接线处映射成 fail 再记,原结论保留在链上不抹历史。
- 推回包必须可执行,缺一项就不算推回完成:审计结论原文;
失败项逐条(含测试名、文件路径、行号、合同条款、diff 范围);
复现步骤;修复验收标准(即重新审计时要过的同一批检查,
@@ -723,10 +727,12 @@ tests/ # QA 线程专属,含跨端一致性用例
不因“是修复”就绕过配额。
- 状态与账本:每轮推回、修复、重审都要写审计记录(轮次、触发项、
推回包、修复 diff、重审结果),账本走第 4 轨的防篡改机制;
本轨先按“审计记录字段 + 校验接口”定义(代码里
`build_ledger_entry` / `validate_ledger_chain` 的
`prev_hash` / `entry_hash` 链式格式是提案,第 4 轨落地后接线,
接口不一致处在 PR 摘要写清)。任务时间线要能看到
接线已落地(`cloud/audit_wiring.py`):推回/修复/重审/
推倒/升级按该模块的映射表写进第 4 轨哈希链,链式校验走
第 4 轨的 `verify_chain` / 导出头哈希;本轨的
`build_ledger_entry` / `validate_ledger_chain` 仍是纯函数
提案格式(供离线校验),链上权威格式以第 4 轨为准。
任务时间线要能看到
“审计 fail → 自动推回 → 修复 → 重审”的完整链路,不能只有
最终通过一条记录把失败轮次抹掉。
- 与已有回路的关系,不重复造轮子:G5 缺陷回路(缺陷回 G4、
@@ -837,10 +843,10 @@ tests/ # QA 线程专属,含跨端一致性用例
- 模块失败计数(审核/验收分来源、合计)、推回轮次、推倒次数、
重做新旧实现对应关系、根因判定结果都要写审计记录,走第 4 轨
的防篡改机制(本轨先按字段 + 校验接口定义:账本条目含
`module_id`、`contract_version`、`fail_counts`、`rebuild_no`、
`root_cause`、`old_implementation_ref`,链式校验见代码
`build_ledger_entry` / `validate_ledger_chain`,第 4 轨落地后接线)。
的防篡改机制(接线已落地,见 `cloud/audit_wiring.py` 的
映射表:账本条目含 `module_id`、`contract_version`、
`fail_counts`、`rebuild_no`、`root_cause`、
`old_implementation_ref`,链式校验走第 4 轨)。
- 任务时间线要能看到完整链路:“审计 fail → 自动推回修复
(1–3 轮)→ 超过 3 次 → 推倒重做 → 重新审计”,不能只有最终
通过一条记录;推倒记录、重新冻结升级记录同样留痕,不得抹掉。